Open logan70 opened 4 years ago
在分析之前,有必要了解一下Async/Await在事件循环中的表现,先看如下代码。
Async/Await
async function async1() { console.log('a') await async2() console.log('b') } async function async2() { console.log('c') } async1() new Promise((resolve) => { console.log('d') resolve() }).then(() => { console.log('e') })
不同chrome版本表现不同,有以下两种情况:
a c d b e
a c d e b
首先说明:最新ECMAScript规范下,第一种为正确表现,下面解释原因。
最新ECMAScript规范中,await直接使用Promise.resolve()相同语义,也就是说,如果await后跟的是一个Promise,则直接返回Promise本身,如果不是,则使用Promise.resolve包裹后返回,上述代码执行过程可以简化理解为:
await
Promise
Promise.resolve
console.log('a') new Promise(resolve => { console.log('c') resolve() }).then(() => { console.log('b') }) new Promise((resolve) => { console.log('d') resolve() }).then(() => { console.log('e') })
console.log('b')在第一轮事件循环时就加入微任务队列,然后console.log('e')才加入微任务队列,故b的打印顺序在先。
console.log('b')
console.log('e')
b
await后不论是否为Promise,都会产生一个新的Promise,再将后面跟的内容resolve出去。
resolve
其实最初关于async/await的相关规范和上述最新规范中行为是一致的,但是中间有一段时间ECMA规范有一些变化,只不过最后又变了回来
async/await
根据老版规范,上述代码执行过程可以简化理解为:
console.log('a') new Promise((resolve1) => { resolve1(new Promise(resolve2 => { console.log('c') resolve2() })) }).then(() => { console.log('b') }) new Promise((resolve) => { console.log('d') resolve() }).then(() => { console.log('e') })
由于resolve1内又resolve了一个Promise,所以在这里已经是异步任务了,而不是立即变为fulfilled的状态,所以console.log('b')并不是在第一轮事件循环中被加入微任务队列,而console.log('e')仍然是在第一轮事件循环中就被加入微任务队列,所以e先于b打印,最终打印顺序为a c d e b。
resolve1
fulfilled
e
更多详细探讨可参考这篇文章。
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') }) console.log('script end')
async1
async2
script start
setTimeout
Web API
async1 start
console.log('async1 end')
new Promise
promise1
console.log('promise2')
script end
async1 end
promise2
故打印顺序为:
深入到ECMA规范了,很强!
复杂异步嵌套逻辑分析
Async/Await 在事件循环中的表现
在分析之前,有必要了解一下
Async/Await
在事件循环中的表现,先看如下代码。不同chrome版本表现不同,有以下两种情况:
a c d b e
a c d e b
首先说明:最新ECMAScript规范下,第一种为正确表现,下面解释原因。
最新ECMAScript规范
最新ECMAScript规范中,
await
直接使用Promise.resolve()相同语义,也就是说,如果await
后跟的是一个Promise
,则直接返回Promise
本身,如果不是,则使用Promise.resolve
包裹后返回,上述代码执行过程可以简化理解为:console.log('b')
在第一轮事件循环时就加入微任务队列,然后console.log('e')
才加入微任务队列,故b
的打印顺序在先。老版ECMAScript规范
await
后不论是否为Promise
,都会产生一个新的Promise
,再将后面跟的内容resolve
出去。根据老版规范,上述代码执行过程可以简化理解为:
由于
resolve1
内又resolve
了一个Promise
,所以在这里已经是异步任务了,而不是立即变为fulfilled
的状态,所以console.log('b')
并不是在第一轮事件循环中被加入微任务队列,而console.log('e')
仍然是在第一轮事件循环中就被加入微任务队列,所以e
先于b
打印,最终打印顺序为a c d e b
。更多详细探讨可参考这篇文章。
复杂异步嵌套分析
async1
、async2
,打印script start
;setTimeout
,回调交由Web API
处理,Web API
将其加入宏任务队列;async1
,打印async1 start
;async2
,打印async2
,由于左边有await
,将console.log('async1 end')
放入微任务队列;new Promise
,同步执行传入构造函数的函数,打印promise1
;console.log('promise2')
所在函数放入微任务队列;script end
,当前任务执行完毕;async1 end
、打印promise2
;setTimeout
;故打印顺序为:
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout