复杂异步嵌套逻辑分析
async/await 在事件循环中的表现
对于不同Chrome 版本,async/await 会有两种表现,如下代码
1 | async function async1() { |
不同的 Chrome 版本,会输出两种结果
- 1 3 4 2 5
- 1 3 4 5 2
根据 最新的 ECMAScript 规范下,第一种为正确表现
最新的 ECMAScript 规范
最新的 ECMAScript 规范中,await 直接使用 Promise.resolve() 相同的语义,也就是说,如果 await 后面跟的是一个 Promise,则直接返回 Promise 本身。如果不是,则使用 Promise.resolve 包裹后返回。
所以上面的代码我们可以理解为:
1 | console.log(1) |
console.log(2) 在第一轮事件循环时就加入微任务队列,然后 console.log(5) 才加入微任务队列,所以 2 的打印顺序在前。
老版的 ECMAScript 规范
await 后不论是否为 Promise,都会产生一个新的 Promise,再将后面的内容 resolve 出去
其实最初关于 async/await 的相关规范和上述最新规范中行为是一致的,但是中间有一段时间 ECMA 规范有一些变化,最后又变回来了。
根据老版规范,上述代码又可以理解成
1 | console.log(1) |
由于 resolve1 内又 resolve 了 一个 Promise,所以在这里已经是异步任务了,而不是立即变为 fulfilled 的状态,所以 console.log(2) 并不是在第一轮事件循环中被加入微任务队列,而console.log(5) 是第一轮事件循环中就被加入到任务队列,最终打印顺序为 1 3 4 5 2
复杂异步嵌套
根据上面的分析来做套题,帮助理解
1 | async function async1() { |
script start
async1 start
async2
promise1
script end
setTimeout
async2 end
promise2
输出结果:script start、async1 start、async2、promise1、script end、async2 end、promise2、setTimeout
- 定义了 async1、async2 函数,打印 script start
- 执行 setTimeout 回调,交由 web API, web API 将它挂入宏任务队列,由下一个宏任务中执行
- 执行 async1 函数,打印 async1 start
- 执行 async2 函数,打印 async2,将 async2 end 挂入 微任务队列
- 执行 new Promise ,同步执行传入构造函数的函数, 打印 promise1,将 promise2 挂入 微任务队列
- 打印 script end,宏任务 执行完毕
- 执行微任务 打印 async2 end
- 执行微任务 打印 promise2,微任务执行完毕,一次事件循环结束
- 执行下一个宏任务,查看宏任务队列,打印 setTimeout。
- 宏任务和 微任务都执行完毕