对async/await的理解,以及内部原理

2,287 阅读3分钟

走起,来吧少年

1、基本原理

async / await 本质上就是 generator 的语法糖

  1. 内置执行器,无需手动执行 next() 方法

2、generator

  1. 在了解async 和 await 之前我们先来了解的下generator函数 (es6.ruanyifeng.com/#docs/gener…
  2. ES6 新引入了 Generator 函数,可以通过 yield 关键字,把函数的执行流挂起,通过next()方法可以切换到下一个状态,为改变执行流程提供了可能,从而为异步编程提供解决方案。
function* helloWorldGenerator() {
  yield 'hello';
  yield 'world';
  return 'ending';
}
const hw = helloWorldGenerator();
hw.next() // { value: 'hello', done: false }
hw.next() // { value: 'world', done: false }
hw.next() // { value: 'ending', done: true }
hw.next() // { value: undefined, done: true }
  1. */yieldasync/await看起来其实已经很相似了
  2. 但是Generator的特点:
    • Generator返回的是生成器对象
    • 需要手动调用next()才能执行下一步
  3. 回想asyncawait的特点、
    • async/await自带执行器,不需要手动调用next()就能自动执行下一步
    • async函数返回值是Promise对象
    • await能够返回Promiseresolve/reject的值
  • 基于对Generator的理解,我们可以封装一个伪函数

3、async 和 await模拟实现

要想实现asyncawait 的功能我必须先解决Generator自动执行问题

function* myGenerator() {
    const res1 = yield Promise.resolve(1);
    console.log('res1', res1); // 1
    const res2 = yield Promise.resolve(2);
    console.log('res2', res2); // 2
    const res3 = yield Promise.resolve(3);
    console.log('res3', res3); // 3
  }
  
  // 手动执行迭代器
  const gen = myGenerator()
  gen.next().value.then(val => {

    gen.next(val).value.then(val => {

      gen.next(val).value.then(val => {
   
        gen.next(val)
      })
    })
  })

看到myGenerator有点async 和 await 的感觉了, 但是上面的手动执行代码显得笨拙且复用性不好,据此,我们把上面代码封装下成一个自动执行函数

function runGenerator(gen) {
  var g = gen()  //由于每次gen()获取到的都是最新的迭代器,因此获取迭代器操作要放在_next()之前,否则会进入死循环
  function _next(val) {             //封装一个方法, 递归执行g.next()
    var res = g.next(val)           //获取迭代器对象,并返回resolve的值
    if(res.done) return res.value   //递归终止条件
    res.value.then(val => {         //Promise的then方法是实现自动迭代的前提
      _next(val)                    //等待Promise完成就自动执行下一个next,并传入resolve的值
    })
  }
  _next()  //第一次执行
}

然后基于上面的函数改写后

function* myGenerator() {
    const res1 = yield Promise.resolve(1);
    console.log('res1', res1); // 1
    const res2 = yield Promise.resolve(2);
    console.log('res2', res2); // 2
    const res3 = yield Promise.resolve(3);
    console.log('res3', res3); // 3
  }
  
  const gen = runGenerator(myGenerator); // 
  console.log(gen); //undefined (还需要实现返回promise)
  • 这样我们就初步实现了一个async/await。
  • 简单说就是封装一个自动generatornext()的方法,每次Promise.then()的时候都去执行_next(),实现自动迭代的效果。

4、返回promise和异常处理

基于上面代码还存在3个问题

  1. 自动执行函数需要返回一个promiseduixiang
  2. yield后面目前跟的是Promise.resove(),我们需要支持普通的级别类型值
  3. 异常的处理
function runGenerator(gen) {
  //把返回值包装成promise
  return new Promise((resolve, reject) => {
    var g = gen()

    function _next(val) {
      //错误处理
      try {
        var res = g.next(val) 
      } catch(err) {
        return reject(err); 
      }
      if(res.done) {
        return resolve(res.value);
      }
      //res.value包装为promise,以兼容yield后面跟基本类型的情况
      Promise.resolve(res.value).then(
        val => {
          _next(val);
        }, 
        err => {
          //抛出错误
          g.throw(err)
        });
    }
    _next();
  });
}

然后再次执行下

function* myGenerator() {
    try {
        const res1 = yield Promise.resolve(1);
        console.log('res1', res1); // 1
        const res2 = yield 2;
        console.log('res2', res2); // 2
        const res3 = yield Promise.reject('error');
        console.log('res3', res3); // 3
      } catch (error) {
        console.log(error)
      }
  }
  
  const gen = runGenerator(myGenerator); 
  console.log(gen); //promise
//输出 1 2 error
  • 好了,今天就介绍到这里,下面我来总结下今天的主要内容。
  • Promise 的编程模型依然充斥着大量的 then 方法,虽然解决了 回调地狱 的问题,但是在语义方面依然存在缺陷,代码中充斥着大量的 then 函数,这就是 async / await 出现的原因。
  • 使用 async/await 可以实现用同步代码的风格来编写异步代码,这是因为 async/await 的基础技术使用了generatorPromise

1、结语

感谢各位老铁,点赞加关注