回调地狱的解决方式总结

异步编程在JavaScript中非常重要,过多的异步编程也带了回调嵌套的问题。【回调地狱】

1、拆解function:将各步拆解为单个的function

2、事件发布/监听模式:

一方面,监听某一事件,当事件发生时,进行相应回调操作;另一方面,当某些操作完成后,通过发布事件触发回调,这样就可以将原本捆绑在一起的代码解耦。


3、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);
});

4、generator

generator是es6中的一个新的语法。在function关键字后添加*即可将函数变为generator。

const gen = function* () {
    yield 1;
    yield 2;
    return 3;
}

执行generator将会返回一个遍历器对象,用于遍历generator内部的状态。

let g = gen();
g.next(); // { value: 1, done: false }
g.next(); // { value: 2, done: false }
g.next(); // { value: 3, done: true }
g.next(); // { value: undefined, done: true }

可以看到,generator函数有一个最大的特点,可以在内部执行的过程中交出程序的控制权,yield相当于起到了一个暂停的作用;而当一定情况下,外部又将控制权再移交回来。
想象一下,我们用generator来封装代码,在异步任务处使用yield关键词,此时generator会将程序执行权交给其他代码,而在异步任务完成后,调用next方法来恢复yield下方代码的执行。以readFile为例,大致流程如下:

// 我们的主任务——显示关键字// 使用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));

5、async/await

可以看到,上面的方法虽然都在一定程度上解决了异步编程中回调带来的问题。然而:

  • function拆分的方式其实仅仅只是拆分代码块,时常会不利于后续维护;
  • 事件发布/监听方式模糊了异步方法之间的流程关系;
  • Promise虽然使得多个嵌套的异步调用能够通过链式的API进行操作,但是过多的then也增加了代码的冗余,也对阅读代码中各阶段的异步任务产生了一定干扰;
  • 通过generator虽然能提供较好的语法结构,但是毕竟generator与yield的语境用在这里多少还有些不太贴切。
    因此,这里再介绍一个方法,它就是es7中的async/await。
    简单介绍一下async/await。基本上,任何一个函数都可以成为async函数,以下都是合法的书写形式:
async function foo () {};
const foo = async function () {};
const foo = async () => {};
在async函数中可以使用await语句。await后一般是一个Promise对象。
async function foo () {
    console.log('开始');
    let res = await post(data);
    console.log(`post已完成,结果为:${res}`);
};

当上面的函数执行到await时,可以简单理解为,函数挂起,等待await后的Promise返回,再执行下面的语句。
值得注意的是,这段异步操作的代码,看起来就像是“同步操作”。这就大大方便了异步代码的编写与阅读。下面改写我们的例子。

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');

可以看到,代码简洁清晰,异步代码也具有了“同步”代码的结构。
注意,其中readFile、queryDB与getData方法都需要返回一个Promise对象。这可以通过在第三部分Promise里提供的方式进行改写。

  • 8
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值