《前端面試手記》之談?wù)刾romise/async/await的執(zhí)行順序

?? 內(nèi)容速覽 ??

  • 題目和答案
  • 輸出解釋
  • 再談?wù)刟sync/await
  • 最新的v8和谷歌瀏覽器的正確輸出

專注前端與算法的系列干貨分享,歡迎關(guān)注(???):
「微信公眾號(hào):心譚博客」| xin-tan.com | GitHub

1. 題目和答案

故事還是要從下面這道面試題說(shuō)起:請(qǐng)問(wèn)下面這段代碼的輸出是什么?

console.log('script start')

async function async1() {
  await async2()
  console.log('async1 end')
}

async function async2() {
  console.log('async2 end')
}
async1()

setTimeout(function() {
  console.log('setTimeout')
}, 0)

new Promise(resolve => {
  console.log('Promise')
  resolve()
})
  .then(function() {
    console.log('promise1')
  })
  .then(function() {
    console.log('promise2')
  })

console.log('script end')

上述,在Chrome 66node v10中,正確輸出是:

script start
async2 end
Promise
script end
promise1
promise2
async1 end
setTimeout

注意:在新版本的瀏覽器中,await輸出順序被“提前”了,請(qǐng)看官耐心慢慢看。

2. 流程解釋

邊看輸出結(jié)果,邊做解釋吧:

  1. 正常輸出script start
  2. 執(zhí)行async1函數(shù),此函數(shù)中又調(diào)用了async2函數(shù),輸出async2 end。回到async1函數(shù),遇到了await,讓出線程
  3. 遇到setTimeout,扔到下一輪宏任務(wù)隊(duì)列
  4. 遇到Promise對(duì)象,立即執(zhí)行其函數(shù),輸出Promise。其后的resolve,被扔到了微任務(wù)隊(duì)列
  5. 正常輸出script end
  6. 此時(shí),此次Event Loop宏任務(wù)都執(zhí)行完了。來(lái)看下第二步被扔進(jìn)來(lái)的微任務(wù),因?yàn)?code>async2函數(shù)是async關(guān)鍵詞修飾,因此,將await async2后的代碼扔到微任務(wù)隊(duì)列中
  7. 執(zhí)行第4步被扔到微任務(wù)隊(duì)列的任務(wù),輸出promise1promise2
  8. 執(zhí)行第6步被扔到微任務(wù)隊(duì)列的任務(wù),輸出async1 end
  9. 第一輪EventLoop完成,執(zhí)行第二輪EventLoop。執(zhí)行setTimeout中的回調(diào)函數(shù),輸出setTimeout。

3. 再談async和await

細(xì)心的朋友肯定會(huì)發(fā)現(xiàn)前面第6步,如果async2函數(shù)是沒(méi)有async關(guān)鍵詞修飾的一個(gè)普通函數(shù)呢?

// 新的async2函數(shù)
function async2() {
  console.log('async2 end')
}

輸出結(jié)果如下所示:

script start
async2 end
Promise
script end
async1 end
promise1
promise2
setTimeout

不同的結(jié)果就出現(xiàn)在前面所說(shuō)的第6步:如果await函數(shù)后面的函數(shù)是普通函數(shù),那么其后的微任務(wù)就正常執(zhí)行;否則,會(huì)將其再放入微任務(wù)隊(duì)列。

4. 其實(shí)是V8引擎的BUG

看到前面,正常人都會(huì)覺(jué)得真奇怪?。ǖ前凑丈厦娴脑E竅倒也是可以理解)

然而V8團(tuán)隊(duì)確定了這是個(gè)bug(很多強(qiáng)行解釋要被打臉了),具體的PR請(qǐng)看這里。好在,這個(gè)問(wèn)題已經(jīng)在最新的Chrome瀏覽器中被修復(fù)了。

簡(jiǎn)單點(diǎn)說(shuō),前面兩段不同代碼的運(yùn)行結(jié)果都是:

script start
async2 end
Promise
script end
async1 end
promise1
promise2
setTimeout

await就是讓出線程,其后的代碼放入微任務(wù)隊(duì)列(不會(huì)再多一次放入的過(guò)程),就這么簡(jiǎn)單了。

更多系列文章

專注前端與算法的系列干貨分享,歡迎關(guān)注(???)


image
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容