async 是 Generator 的语法糖

2,464 阅读2分钟

async 是 Generator 的语法糖,用 async 比 Generator 的方便之处在哪呢?下面分别用 async 和 Generator 实现同一个场景,来说明 async 的优异之处。

场景:有一个异步任务需要花 3s 才能执行完,执行完后,执行回调操作。

Generator 实现

直接上代码:

function* gen() {
  yield new Promise(function(resolve, reject) {
	setTimeout(resolve, 3000);
  }).then(() => { it.next() });
  console.log('回调操作可以执行啦!');
}

const it = gen();
it.next();
console.log('同步任务');

代码执行结果:

Generator 实现
下面分析这段代码的执行过程:

  1. 执行 gen 函数,得遍历器对象 it;
  2. 执行 it.next(),执行 yield 后面的语句 new Promise(),并且 it 的状态停留在 yield 这了;
  3. 执行 new Promise(),将宏任务 setTimeout 放到 Event Table 中,并注册回调函数 resolve;
  4. 执行 console.log('同步任务'),主线程的同步任务执行完了;
  5. 3s 到,将 setTimeout 的回调函数 resolve 放到 Event Queue 中,因为主线程的同步任务执行完了,执行 resolve();
  6. 将 then 方法中的微任务 () => { it.next() } 放到 Event Queue 中,因为主线程没有任务执行,执行 it.next 函数;
  7. 执行 yield 后面的语句,也就是场景中的回调操作;
  8. 结束。

async 实现

直接上代码:

async function asy() {
  await new Promise(function(resolve, reject) {
	setTimeout(resolve, 3000);
  });
  console.log('回调操作可以执行啦!');
}

asy();
console.log('同步任务');

代码执行结果:

async 实现
下面分析这段代码的执行过程:

  1. 执行 asy 函数;
  2. 执行 await 后面的语句 new Promise();
  3. 执行 new Promise(),将宏任务 setTimeout 放到 Event Table 中,并注册回调函数 resolve;
  4. 执行 console.log('同步任务'),主线程的同步任务执行完了;
  5. 3s 到,将 setTimeout 的回调函数 resolve 放到 Event Queue 中,因为主线程的同步任务执行完了,执行 resolve();
  6. 执行 await 后面的语句,也就是场景中的回调操作;
  7. 结束。

总结:

通过对比两段代码的执行过程,可知,yield 后面的异步任务执行完后,需要手动执行 next 方法,才能接着执行 yield 下面的回调操作,await 后面的异步任务执行完后,可以自动执行 await 下面的回调操作,这就是 async 在 Generator 的基础上多做的工作。

另,回调 API、Promise、Generator、async 更多的是代表一种异步编程风格,对于 JS 中的异步编程,本质上还是得去了解 JS 的执行机制。学习 JS 的执行机制,推荐这篇 博客 ,感兴趣的话,可以看下 我的笔记