由一道面試題引出: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
async
3. 遇到settimeout,將其裏面的內容加入宏任務隊列函數
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
原文: