👇 內容速覽 👇javascript
故事仍是要從下面這道面試題提及:請問下面這段代碼的輸出是什麼?
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 66
和node v10
中,正確輸出是:前端
script start async2 end Promise script end promise1 promise2 async1 end setTimeout
注意:在新版本的瀏覽器中,
await
輸出順序被「提早」了,請看官耐心慢慢看。
邊看輸出結果,邊作解釋吧:java
script start
async1
函數,此函數中又調用了async2
函數,輸出async2 end
。回到async1
函數,遇到了await
,讓出線程。setTimeout
,扔到下一輪宏任務隊列 Promise
對象,當即執行其函數,輸出Promise
。其後的resolve
,被扔到了微任務隊列script end
Event Loop
宏任務都執行完了。來看下第二步被扔進來的微任務,由於async2
函數是async
關鍵詞修飾,所以,將await async2
後的代碼扔到微任務隊列中promise1
和promise2
async1 end
setTimeout
中的回調函數,輸出setTimeout
。細心的朋友確定會發現前面第6步,若是async2
函數是沒有async
關鍵詞修飾的一個普通函數呢?node
// 新的async2函數 function async2() { console.log('async2 end') }
輸出結果以下所示:webpack
script start async2 end Promise script end async1 end promise1 promise2 setTimeout
不一樣的結果就出如今前面所說的第6步:若是await函數後面的函數是普通函數,那麼其後的微任務就正常執行;不然,會將其再放入微任務隊列。css3
看到前面,正常人都會以爲真奇怪!(可是按照上面的訣竅倒也是能夠理解)git
然而V8團隊肯定了這是個bug(不少強行解釋要被打臉了),具體的PR請看這裏。好在,這個問題已經在最新的Chrome瀏覽器中被修復了。es6
簡單點說,前面兩段不一樣代碼的運行結果都是:github
script start async2 end Promise script end async1 end promise1 promise2 setTimeout
await
就是讓出線程,其後的代碼放入微任務隊列(不會再多一次放入的過程),就這麼簡單了。
《前端知識體系》
《設計模式手冊》
《Webpack4漸進式教程》