async和await实现原理

272 阅读3分钟

我们一直使用async和await,来让我们以同步的方式来调用异步代码,它实际上就是promise语法糖,书写起来也比较方便。一直都是使用,但是并不知道它的实现原理是什么,今天再看es6文档的时候看到了,就简单来记录一下。

引导

其实这个问题看似很难,但是其实是我们应该知道的一个问题,比如说我们在学习es6的时候有一个generator生成器函数,随后我们就开始学习了async和await,我记得当时我看视频中老师说了把generator的*换成async把yield换成await,就是async和await 由此我们可见,async和await的实现和生成器函数密不可分

生成器函数详解

因为我们平常用生成器函数比较少,所以可能有些遗忘,所以我这里来简单的回顾一下

  1. 函数声明
// 注意,生成器函数的星号不受两侧空格的影响
function* generatorFn() {}
  1. 使用

声明生成器之后,我们需要调用生成器函数来产生一个生成器对象,然后控制整个函数的调度,另外需要注意的一点是:生成器一开始处于暂停执行,我们需要先调用,才能执行(这里也就牵涉到为什么next第一次传入的值没有效果,是因为是为了开启调度的),与生成器相似的是,生成器对象也实现了iterator接口,会有next()方法(该方法用于调度到下一个),同时返回值会有一个done(ture表示调度结束了)和value

//直接使用
function* generatorFn() { 
  return 'foo'; 
} 
let generatorObject = generatorFn(); 
console.log(generatorObject); // generatorFn {<suspended>} 
console.log(generatorObject.next()); // { done: true, value: 'foo' }
// 使用yield中断
function* generatorFn() { 
  yield 'foo'; 
  yield 'bar'; 
  return 'baz'; 
} 
let generatorObject = generatorFn(); 
console.log(generatorObject.next()); // { done: false, value: 'foo' } 
console.log(generatorObject.next()); // { done: false, value: 'bar' } 
console.log(generatorObject.next()); // { done: true, value: 'baz' }
// 使用next传值
function* generatorFn(initial) { 
  console.log(initial); 
  console.log(yield); 
  console.log(yield); 
} 
// 生成一个生成器对象
let generatorObject = generatorFn('foo');
// 开启调度,执行第一个输出,输出传入的initial
generatorObject.next('bar'); // foo 
// 调度下一个,next传入值会作为上一个yield的返回值(即第二个next传入的值会作为第一个yield的返回值)
generatorObject.next('baz'); // baz 
// 输出qux,解释同上一个
generatorObject.next('qux'); // qux

/*
这里可能比较绕一点,我们只需要记住第一次调用next传入的值是无效的
因为第一次的next是启动调度,之后调用的next传入的值会作为yield的返回值
比如我们第二次调用next('baz')会输出yield
*/

实现async和await

function spawn(genF) {
  return new Promise(function (resolve, reject) {
    const gen = genF();
    function step(nextF) {
      let next;
      try {
        next = nextF();
      } catch (e) {
        return reject(e);
      }
      // done是false表示已经没有后续的代码要运行了
      if (next.done) {
        return resolve(next.value);
      }
      Promise.resolve(next.value).then(function (v) {
        // 开始调度,这里next传递的值会作为上一次yeild的返回值(即第二次执行next函数会作为第一个yield的返回值因为第一个没有用)
        step(function () { return gen.next(v); });
      }, function (e) {
        step(function () { return gen.throw(e); });
      });
    }
    // 开始调度,第一次next传递的值没有效果,用于开始调度
    step(function () { return gen.next(undefined); });
  });
}

以上就是async和await的实现原理,我们在调用的时候直接传入一个生成器函数就可以