Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

执行机制 - 复杂异步嵌套逻辑分析 #35

Open
logan70 opened this issue Dec 11, 2019 · 1 comment
Open

执行机制 - 复杂异步嵌套逻辑分析 #35

logan70 opened this issue Dec 11, 2019 · 1 comment

Comments

@logan70
Copy link
Owner

logan70 commented Dec 11, 2019

复杂异步嵌套逻辑分析

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规范

最新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')
  1. 定义函数async1async2打印script start
  2. 执行setTimeout,回调交由Web API处理,Web API将其加入宏任务队列;
  3. 执行async1打印async1 start
  4. 执行async2打印async2,由于左边有await,将console.log('async1 end')放入微任务队列;
  5. 执行new Promise,同步执行传入构造函数的函数,打印promise1
  6. promise完成,将console.log('promise2')所在函数放入微任务队列;
  7. 打印script end,当前任务执行完毕;
  8. 检查微任务队列并依次取出执行,打印async1 end打印promise2
  9. 微任务队列为空,执行栈为空,检查宏任务队列,取出任务执行,打印setTimeout
  10. 执行完毕。

故打印顺序为:

  • script start
  • async1 start
  • async2
  • promise1
  • script end
  • async1 end
  • promise2
  • setTimeout
@hansenwangvip
Copy link

深入到ECMA规范了,很强!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants