前言
对于异步编程来说, async/await
大大简化了异步操作,异步操作可以写成同步写法,如果不了解 async/await
可以点击🔗MDN async 函数了解更多关于 async
知识
编译async/await
在平时工作中,可能会遇到 async/await
,比如有这样的例子
把 a
函数定义为async
,同时在内部使用 await
,并把 await
的返回结果作为普通函数 b
的实参
来看这个 async
函数 a
编译后的代码
解析
首先在 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*
js
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
函数上来, 那么只要是 done
为 true
的情况,说明 生成器函数
已经执行完毕,这时候 Promise
就可以结束了,如果没有结束,那么就需要不断的执行迭代器,直到 done
为 true
剩下的就是不断执行器,直到 done
为 true
执行 adopt
这个辅助方法,传入执行迭代器
返回的值,adopt
返回 Promise
,然后再把 resolve
后的值传递给内部的 fulfilled
方法,在 fulfilled
中继续执行 step
方法
由于 P 是 void 0,所以 P 被赋值为
Promise
,adopt
定义为Promise
是为了解决异步问题
根据核心逻辑作了一些简化
js
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
的认识
碰见 async
就当做普通函数对待,因为只有 await
才会被转化为 yield
, 所以 asyncfn1
可以看做
js
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