-
Notifications
You must be signed in to change notification settings - Fork 8
Open
Labels
Description
复杂异步嵌套逻辑分析
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 ea c d e b
首先说明:最新ECMAScript规范下,第一种为正确表现,下面解释原因。
最新ECMAScript规范
最新ECMAScript规范中,await直接使用Promise.resolve()相同语义,也就是说,如果await后跟的是一个Promise,则直接返回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的打印顺序在先。
老版ECMAScript规范
await后不论是否为Promise,都会产生一个新的Promise,再将后面跟的内容resolve出去。
其实最初关于
async/await的相关规范和上述最新规范中行为是一致的,但是中间有一段时间ECMA规范有一些变化,只不过最后又变了回来
根据老版规范,上述代码执行过程可以简化理解为:
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。
更多详细探讨可参考这篇文章。
复杂异步嵌套分析
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处理,Web API将其加入宏任务队列; - 执行
async1,打印async1 start; - 执行
async2,打印async2,由于左边有await,将console.log('async1 end')放入微任务队列; - 执行
new Promise,同步执行传入构造函数的函数,打印promise1; - promise完成,将
console.log('promise2')所在函数放入微任务队列; - 打印
script end,当前任务执行完毕; - 检查微任务队列并依次取出执行,打印
async1 end、打印promise2; - 微任务队列为空,执行栈为空,检查宏任务队列,取出任务执行,打印
setTimeout; - 执行完毕。
故打印顺序为:
script startasync1 startasync2promise1script endasync1 endpromise2setTimeout
zhanghuize-git