了解async await

吐槽君 分类:javascript

先看以下代码执行结果:

const funA = () => {
    console.log('funA-1')
    const p = new Promise((resolve) => {
        resolve('funA-2')
    })
    console.log('funA-3')
    return p
}

const funB = () => {
    console.log('funB-1')
    return 'funB-2'
}

const funB2 = () => {
    console.log('funB2-1')
    return 'funB2-2'
}

const funC = async () => {
    console.log('funC-1')

    const p = await funA()
    console.log(p)

    console.log('funC-2')

    const b = await funB()
    console.log(b)

    console.log('funC-3')

    const b2 = await funB2()
    console.log(b2)

    console.log('funC-4')

}

console.log('start')
funC()
console.log('end')
 

截屏2021-03-31 22.42.22.png

注意,先 end 再 funA-2

即执行完await右边的微任务,会先去执行调用该外层函数的作用域的未完成的同步任务

要想了解async await的内部实现原理,那你先必须了解generator函数

function  * gen(x) {
    console.log('111111')
    const a = 5
    const b = yield a * 2
    console.log('-----b: ', b)
    const c = yield x - 1
    console.log('-----c: ', c)
    return b
}

const g = gen(1)
console.log(g)
console.log(g.next())
console.log(g.next())
console.log(g.next(8))
console.log(g.next(6))
console.log(g.next())
 

截屏2021-03-31 23.07.58.png

  • gen() 不会立即执行,而是一上来就暂停,返回一个 Iterator 对象(具体可以参考 Iterator遍历器)
  • 每次 g.next() 都会打破暂停状态去执行,直到遇到下一个 yield 或者 return
  • 遇到 yield 时,会执行 yeild 后面的表达式,并返回执行之后的值,然后再次进入暂停状态,此时 done: false。
  • next 函数可以接受参数,作为上个阶段异步任务的返回结果,被函数体内的变量接收
  • 遇到 return 时,会返回值,执行结束,即 done: true
  • 每次 g.next() 的返回值永远都是 {value: ... , done: ...} 的形式

async/await与generator的区别

  • async/await自带执行器,不需要手动调用next()就能自动执行下一步
  • async函数返回值是Promise对象,而Generator返回的是生成器对象
  • await能够返回Promise的resolve/reject的值

async/await是自带执行器的generator,通过Promise未完成则then继续调用next,完成则resolve。

简版:

function run(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() {
  console.log(yield Promise.resolve(1))   //1
  console.log(yield Promise.resolve(2))   //2
  console.log(yield Promise.resolve(3))   //3
}

run(myGenerator)
 

扩展:增加基本类型;增加错误处理

function run(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();
  });
}
 

babel转译版:

//相当于我们的run()
function _asyncToGenerator(fn) {
  // return一个function,和async保持一致。我们的run直接执行了Generator,其实是不太规范的
  return function() {
    var self = this
    var args = arguments
    return new Promise(function(resolve, reject) {
      var gen = fn.apply(self, args);

      //相当于我们的_next()
      function _next(value) {
        asyncGeneratorStep(gen, resolve, reject, _next, _throw, 'next', value);
      }
      //处理异常
      function _throw(err) {
        asyncGeneratorStep(gen, resolve, reject, _next, _throw, 'throw', err);
      }
      _next(undefined);
    });
  };
}

function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
  try {
    var info = gen[key](arg);
    var value = info.value;
  } catch (error) {
    reject(error);
    return;
  }
  if (info.done) {
    resolve(value);
  } else {
    Promise.resolve(value).then(_next, _throw);
  }
}

 

参考:

zhuanlan.zhihu.com/p/115112361
segmentfault.com/a/119000002…

回复

我来回复
  • 暂无回复内容