问题描述
今天看到一个关于js执行顺序的问题,不太了解async await中await后的代码的执行时机
- 问题1. 为啥promise2、promise3输出比async1 end输出早?如果都是微任务的话,不是async1 end先加入微任务队列的吗?
- 问题2. 为什么async1 end又先于promise4输出呢?
相关代码
async function async1(){
console.log('async1 start')
await async2()
console.log('async1 end')
}
async function async2(){
console.log('async2')
}
console.log('script start')
setTimeout(function(){
console.log('setTimeout')
},0)
async1();
new Promise(function(resolve){
console.log('promise1')
resolve();
}).then(function(){
console.log('promise2')
}).then(function() {
console.log('promise3')
}).then(function() {
console.log('promise4')
}).then(function() {
console.log('promise5')
}).then(function() {
console.log('promise6')
}).then(function() {
console.log('promise7')
}).then(function() {
console.log('promise8')
})
console.log('script end')
chrome 70.0.3538.102 结果
script start
async1 start
async2
promise1
script end
promise2 // 与 chrome canary 73 不一致
promise3 // 与 chrome canary 73 不一致
async1 end // 与 chrome canary 73 不一致
promise4
promise5
promise6
promise7
promise8
setTimeout
Chrome canary 73.0.3646.0(同node8.12.0):
script start
async1 start
async2
promise1
script end
async1 end // 与 chrome 70 不一致
promise2 // 与 chrome 70 不一致
promise3 // 与 chrome 70 不一致
promise4
promise5
promise6
promise7
promise8
setTimeout
基础知识
在你看答案之前,我希望你至少了解
promise
的 executor(执行器) 里的代码是同步的promise
的回调是 microTask(微任务) 而setTimeout
的回调是 task(任务/宏任务)精简题目
我把这道题精简下,先把同步的代码和
setTimeout
的代码删掉,再来解释(期间await
的部分规范有变动)。再精简下,问题就是这样:
为什么在 chrome canary 73 返回
而在 chrome 70 上返回
正文
我对
promise
稍微熟悉些,其实也不熟,但是把await
转成promise
会相对好理解些,不知道有没有同感?这道题其实问的是
因为
async
函数总是返回一个promise
,所以其实就是在问那么我们看下规范 Await
根据提示
等价于
与@Jialiang_T同学给出的一致,但是到这里,仍然不太好理解,
为了行文方便,这里开始我们用
RESOLVE
来表示Promise
构造器里的resolve
,例如:之所以这样,因为
async2()
返回一个promise
, 是一个thenable
对象,RESOLVE(thenable)
并不等于Promise.resolve(thenable)
,而RESOLVE(non-thenable)
等价于Promise.resolve(non-thenable)
,具体对照规范的解释请戳结论就是:
RESOLVE(thenable)
和Promise.resolve(thenable)
的转换关系是这样的,会被转换成
那么对于
RESOLVE(async2())
,我们可以根据规范转换成:所以
async1
就变成了这样:同样,因为
RESOLVE()
就等价于Promise.resolve()
,所以等价于
所以,题目
就等价于
这就是根据当前规范解释的结果, chrome 70 和 chrome canary 73 上得到的都是一样的。
Await 规范的更新
那么为什么,chrome 73 现在得到的结果不一样了呢?修改的决议在这里,目前是这个状态。
就像你所看到的一样,为什么要把
async1
转换成
而不是直接
这样是不是更简单直接,容易理解,且提高性能了呢?如果要这样的话,也就是说,
async1
不采用new Promise
来包装,也就是不走下面这条路:而是直接采用
Promise.resolve()
来包装,也就是又因为
async2()
返回一个promise
, 根据规范Promise.resolve,所以
Promise.resolve(promise)
返回promise
, 即Promise.resolve(async2())
等价于async2()
,所以最终得到了代码这就是贺老师在知乎里所说的
tc39 的 spec 的更改体现在
chrome canary 73 采用了这种实现,所以题目
在 chrome canary 73及未来可能被解析为
在 chrome 70 被解析为,
转换后的代码,你应该能够看得懂了,如果看不懂,说明你需要补一补 promise 的课了。
2018.12.26
resolve(thenable)
和Promise.resolve(thenable)
的思考,也就是 SO 的那个问题。async
的转换分析,不过是英文的,自己斟酌要不要去看看,如果觉得不错的话,留言,我可以再翻译成中文。