1、什么是回调函数?
回调函数一个通过指针调用的函数。如果你把这个指针作为参数传给另一个函数,当这个指针指向的函数被调用的时候,我们就说和这个函数是回调函数。回调函数不是由函数的实现方直接调用的,而是在特定的时间或者条件,由另一方函数调用。
2、回调函数的作用
把调用者和被调用者区分开,所用调用者不关心谁是被调用者,它只需知道存在一个具有特定原型的和限制条件的被调用者函数。简而言之,回调函数允许用户把需要调用的函数作为一个指针传给另一个函数,使程序更加灵活。
3、回调函数的缺点:回调地狱
回调地址的本质:①嵌套函数存在严重的耦合,牵一发而动全身。②错误处理比较艰难,不能使用try catch 和不能直接return。
4、如何解决回调地狱?
三个原则
- 保持代码浅
- 模块化
- 处理每一个错误
实现
- 拆解function,将各部拆解为一个一个的function。
- 事件发布/监听模式
一方面监听事件发生,如果发生执行相应的回调,另一方面,监听操作的完成,当操作完成时进行相应的回调。
- Promise
readFile('./sample.txt').then(content => {
let keyword = content.substring(0, 5);
return queryDB(keyword);
}).then(res => {
return getData(res.length);
}).then(data => {
console.log(data);
}).catch(err => {
console.warn(err);
});
- generator
// 我们的主任务——显示关键字// 使用yield暂时中断下方代码执行// yield后面为promise对象
const showKeyword = function* (filepath) {
console.log('开始读取');
let keyword = yield readFile(filepath);
console.log(`关键字为${filepath}`);
}
// generator的流程控制
let gen = showKeyword();
let res = gen.next();
res.value.then(res => gen.next(res));
- async/await
可以看到,上面的方法虽然都在一定程度上解决了异步编程中回调带来的问题。
- function拆分的方式其实仅仅只是拆分代码块,时常会不利于后续维护;
- 事件发布/监听方式模糊了异步方法之间的流程关系;
- Promise虽然使得多个嵌套的异步调用能够通过链式的API进行操作,但是过多的then也增加了代码的冗余,也对阅读代码中各阶段的异步任务产生了一定干扰;
- 过generator虽然能提供较好的语法结构,但是毕竟generator与yield的语境用在这里多少还有些不太贴切。
因此,这里再介绍一个方法,它就是es7中的async/await。
简单介绍一下async/await。基本上,任何一个函数都可以成为async函数,以下都是合法的书写形式:
const printData = async function (filepath) {
let keyword = await readFile(filepath);
let count = await queryDB(keyword);
let data = await getData(res.length);
console.log(data);
});
printData('./sample.txt');