async/await 原理解析

402 阅读4分钟

前言

对于异步编程来说, async/await大大简化了异步操作,异步操作可以写成同步写法,如果不了解 async/await 可以点击🔗MDN async 函数了解更多关于 async 知识

编译async/await

在平时工作中,可能会遇到 async/await,比如有这样的例子

code.png

a 函数定义为async ,同时在内部使用 await,并把 await 的返回结果作为普通函数 b 的实参

来看这个 async 函数 a 编译后的代码

code.png

🔗查看async/await源码

解析

首先在 async 的函数 a 中,返回了 __awaiter 函数的返回值,并在函数内部传递了4个实参,分别是 this,undefined,undefined,function *

🔔 其中 void 0 可以看做不可变 undefined,因为 undefined 不是关键字,可以被修改,所以使用 void 0 来代替 undefined

__awaiter 是一个高阶函数,在内部返回了一个 Promise

new (P || P = new Promise)(function(){}), 由于 P 是 undefined,所以 P 被赋值给 Promise

Promise 内部中,默认执行了 step 方法,同时把 第四个参数 (即一个生成器函数执行后的 next 对象) 作为实参传递给 step,如果不理解生成器函数的返回结果,可以看下面的例子简单了解一下,更多细节请查看🔗MDN关于# function*

function* gen() {
  yield 10;
  x = yield "foo";
  yield x;
}

var gen_obj = gen();

// { value: 10, done: false }
console.log(gen_obj.next()); // 执行 yield 10
// { value: 'foo', done: false }
console.log(gen_obj.next()); // 执行 yield 'foo'

// { value: 100, done: false }
console.log(gen_obj.next(100)); // 将 100 赋给上一条 yield 'foo' 的左值,即执行 x=100,返回 100
// { value: undefined, done: true }
console.log(gen_obj.next()); // 执行完毕,value 为 undefined,done 为 true

以下解释来自 🔗MDN

调用一个生成器函数并不会马上执行它里面的语句,而是返回一个这个生成器的 迭代器  ( iterator )对象。当这个迭代器的 next() 方法被首次(后续)调用时,其内的语句会执行到第一个(后续)出现yield的位置为止,yield 后紧跟迭代器要返回的值。或者如果用的是 yield*(多了个星号),则表示将执行权移交给另一个生成器函数(当前生成器暂停执行)。

next()方法返回一个对象,这个对象包含两个属性:value 和 done,value 属性表示本次 yield 表达式的返回值,done 属性为布尔类型,表示生成器后续是否还有 yield 语句,即生成器函数是否已经执行完毕并返回。

调用 next()方法时,如果传入了参数,那么这个参数会传给上一条执行的 yield 语句左边的变量

❤️简单来说,通过执行 function* 返回一个迭代器对象, 这个对象有 next 方法,每调用一次 next 方法,就会执行到遇到 yield为止,然后返回一个包含 value 字段和 done 字段 的对象, value 字段代表本次 yield 的返回值,done 表示是否完成,再次调用,遇到下个 yield 会再次停止

function * 其实就是让程序员控制函数的执行步骤

🔗关于生成器方法更多示例代码


回到 step 函数上来, 那么只要是 donetrue 的情况,说明 生成器函数 已经执行完毕,这时候 Promise 就可以结束了,如果没有结束,那么就需要不断的执行迭代器,直到 donetrue

剩下的就是不断执行器,直到 donetrue

执行 adopt 这个辅助方法,传入执行迭代器 返回的值,adopt 返回 Promise,然后再把 resolve 后的值传递给内部的 fulfilled 方法,在 fulfilled 中继续执行 step 方法

由于 P 是 void 0,所以 P 被赋值为 Promise, adopt 定义为 Promise 是为了解决异步问题

根据核心逻辑作了一些简化

code.png

var __awaiter = function (thisArg, generator) {
  return new Promise(function (resolve) {

    function step(result) {
      // 为 true 说明执行完毕,直接结束
      result.done
        ? resolve(result.value)
        //  再次创建一个 Promise,因为 Promise 可以处理异步,然后递归执行 step 方法
        : Promise.resolve(result.value).then((res)=>step(generator.next(res)));
    }

    step((generator = generator.apply(thisArg, undefined)).next());
  });
};


//  async function a() {
//    let r = await 1;
//     await 2;
//     return b(r)
//   }
//  function b(r){
//   return r
//  }
//  
//   a().then(res=>{
//     console.log(res)
//   })
//

function a() {
  return __awaiter(this,function* () {
    let r = yield 1;
    yield 2;
    return b(r);
  });
}

function b(r) {
  return r;
}

a().then(res => {
  console.log(res);
});

最后看一道面试题来加强对 async /await 的认识

code.png

碰见 async 就当做普通函数对待,因为只有 await 才会被转化为 yield, 所以 asyncfn1 可以看做

function asyncfn1(){
   return __awaiter(this,function* () {
    console.log(2)
    let r = yield asyncfn2();
    console.log(3)
  });
}

由于 __awaiter 方法中迭代器函数会被默认执行一次,即 step(generator = generator.apply(thisArg, undefined)).next()),因为 console(2)yield 前面,不受 yield 影响,所以会同步输出, 同样的 asyncfn2 也是同样的道理

所以最终结果是 1 2 4 5 3 6