由一道面試題引出:javascript
//請寫出輸出內容 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'); /* script start async1 start async2 promise1 script end async1 end promise2 setTimeout */複製代碼
在理清楚如下幾個知識點後上面問題就迎刃而解了:java
1. 宏任務:包括script(總體代碼),setTimeout,setInterval,I/O,UI交互,postMessagegit
2. 微任務:包括promise.then,MutationObservergithub
3. 執行過程: a) 執行一個宏任務 b)過程當中遇到微任務,則添加其到微任務隊列 c)當前宏任務執行完以後,開始依次執行全部微任務 d)GUI線程接管開始渲染 e)渲染完成交給js線程重複這個過程面試
4. async標誌的函數裏面,只有當await的表達式返回結果後,await之後的代碼纔會被執行promise
5. async/await實際上是promise的語法糖,因此await之後的代碼至關於放入了then裏面瀏覽器
1. 代碼開始,宏任務是scriptbash
Macro tasks
---------------
| script |
---------------複製代碼
2. 從上往下,首先看到了同步代碼log,則輸出 script start
markdown
3. 遇到settimeout,將其裏面的內容加入宏任務隊列async
Macro tasks ---------------------------------- | script | log 'setTimeout' | ----------------------------------複製代碼
4. 運行async1函數,沒遇到await以前都是同步代碼,輸出 async1 start
5. 遇到await,解析成以下形式
Promise.resolve(async2()).then(() => {console.log('async1 end')})複製代碼
6. async2由於直接返回值無需等待,因此輸出 async2
7. 如上改寫能夠看出那句log被放入了微任務隊列
Macro tasks queue ---------------------------------- | script | log 'setTimeout' | ---------------------------------- Micro tasks queue ----------------------- | log 'async1 end' | -----------------------複製代碼
8. 遇到new promise實例化過程,此過程依然是同步的,輸出promise1
,並把then的內容加入微隊列
Macro tasks queue ---------------------------------- | script | log 'setTimeout' | ---------------------------------- Micro tasks queue ------------------------------------------- | log 'async1 end' | log 'promise2' | -------------------------------------------複製代碼
9. 輸出同步log:script end
10. 此時已經到了script這個宏任務的最後了,開始執行其全部微任務,清空微任務隊列,輸出async1 end
,promise2
Macro tasks queue ---------------------------------- | script | log 'setTimeout' | ---------------------------------- Micro tasks queue ------------------------------------------- | | -------------------------------------------複製代碼
11. script宏作完,本身出隊列
Macro tasks queue ---------------------------------- | log 'setTimeout' | ---------------------------------- Micro tasks queue ------------------------------------------- | | -------------------------------------------複製代碼
12. 瀏覽器開始渲染,渲染完成開始下一個宏任務
13. 輸出setTimeout
, 清空宏隊列,完成!
這種題就算知道了解法,也推薦畫圖,否則很容易暈
async function async1() { console.log('async1 start'); await async2(); console.log('async1 end'); } async function async2() { //async2作出以下更改: new Promise(function(resolve) { console.log('promise1'); resolve(); }).then(function() { console.log('promise2'); }); } console.log('script start'); setTimeout(function() { console.log('setTimeout'); }, 0) async1(); new Promise(function(resolve) { console.log('promise3'); resolve(); }).then(function() { console.log('promise4'); }); console.log('script end');複製代碼
// 答案 script start async1 start promise1 promise3 script end promise2 async1 end promise4 setTimeout複製代碼
settimeout加入宏隊列,promise1在實例化時輸出,promise2和async1 end加入微隊列 Macro tasks queue Micro tasks queue --------------------------- ------------------------------ | script | setTimeout | | promise2 | async1 end | --------------------------- ------------------------------ promise3輸出,promise4加入微隊列 ------------------------- --------------------------------------- | script | settimeout | | promise2 | async1 end | promise4 | ------------------------- ---------------------------------------複製代碼
另外一個
async function async1() { console.log('async1 start'); await async2(); //更改以下: setTimeout(function() { console.log('setTimeout1') },0) } async function async2() { //更改以下: setTimeout(function() { console.log('setTimeout2') },0) } console.log('script start'); setTimeout(function() { console.log('setTimeout3'); }, 0) async1(); new Promise(function(resolve) { console.log('promise1'); resolve(); }).then(function() { console.log('promise2'); }); console.log('script end');複製代碼
// 答案 script start async1 start promise1 script end promise2 setTimeout3 setTimeout2 setTimeout1複製代碼
settimeout3加入宏隊列,settimeout2也加入宏,注意此時後面總體代碼加入微隊列 Macro tasks queue Micro tasks queue ------------------------------------------- ------------------------------ | script | setTimeout3 | settimeout2 | | setTiemout(settimout1) | ------------------------------------------- ------------------------------ promise3輸出,promise4加入微隊列 ----------------------------------------- ----------------------------------------- | script | settimeout3 | settimeout2 | | setTimeout(settimeout1) | promise2 | ----------------------------------------- ----------------------------------------- script end輸出後開始執行微隊列,setTimoue被取出執行,把settimeout1加入宏隊列 -------------------------------------------------- -------------- | script | setimeout3 | settimeou2 | settimeout1 | | promise2 | -------------------------------------------------- -------------複製代碼
另外一個
async function a1 () { console.log('a1 start') await a2() console.log('a1 end') } async function a2 () { console.log('a2') } console.log('script start') setTimeout(() => { console.log('setTimeout') }, 0) Promise.resolve().then(() => { console.log('promise1') }) a1() let promise2 = new Promise((resolve) => { resolve('promise2.then') console.log('promise2') }) promise2.then((res) => { console.log(res) Promise.resolve().then(() => { console.log('promise3') }) }) console.log('script end')複製代碼
// 答案 script start a1 start a2 promise2 script end promise1 a1 end promise2.then promise3 setTimeout複製代碼
這跟上面差很少,只不過把promise2分開寫,實例化是同步的因此先運行log promise2
原文: