Andy的前後端技術筆記、生活紀錄
Javascript的Event Loop以及Micro task, Macro task是如何運作的?
發佈於: 2024-04-16 更新於: 2024-04-21 分類於: frontend > frontend_interview

Event loop是什麼?

在javascript的設計中,javascript runtime是以single Thread單執行在執行程式碼,也就是說程式會順序的執行程式碼,而Javascript 透過 Event Loop 讓程式可以異步執行,也就是I/O存取、setTimeout、長時間操作(遠端資料請求),讓使用者不用在順序執行的程式流一直在等待,那這些在被執行完的異步行為的後續程式碼要如何讓他繼續被執行後續的程式碼呢,這就要透過Event loop來做調配。

Event loop角色

  • Call stack
  • micro task
  • macro task

程式執行順序

依照程式碼一路順序執行,執行的方式就是會把程式碼逐個放進到call stack中執行,當碰到如setTimeout、I/O存取、Fetch、Promise時,會開始執行且將其變成異步執行,也就是說原先的程式碼會繼續執行,當setTimeout、檔案讀取完成,Event loop就會將如setTimeout的callback function放進到task queue(micro task or macro task)中,等待被執行。

什麼時候執行task中的callback function呢?當call stack沒有程式碼要被執行時就會來檢查task queue裡面是否有task要被執行,若有則開始執行,在這裡會有執行的優先順序,會先執行Micro task queue,直到Micro task queue為空時才會去檢查Macro task queue,Event loop會從Macro task中取出一個task來執行,執行完後再檢查Micro Task,若沒有才會再進到Macro task取出一個task執行。

誰會進入Macro / Micro task呢?

  • Macro : setTimeout, setInterval, I/O操作, UI渲染…
  • Micro : Promise.then, Promise.catch, Promise.finally, MutationObserver, process.nextTick()(屬於Node.js)

首先先來基本題,我們先把macro task跟 micro task 看成同一個Queue

1
2
3
4
5
6
7
8
console.log('First');

setTimeout(() => {
console.log('Second');
}, 0);

console.log('Third');

上述會怎麼執行呢?

順序:
First
Third
Sencond

一開始碰到setTimeout被設為0,看似會馬上執行callback function,依照流程此callback function會被放到task queue中等待被執行,而在setTimeout後面還有程式碼要被放到call stack執行,所以得等到他被執行完才會去到Queue把 console.log(‘Second’); 拿出來執行。

進階題

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
console.log('Start');

setTimeout(() => {
console.log('Timeout');
}, 0);

Promise.resolve().then(() => {
console.log('Promise 1');
}).then(() => {
console.log('Promise 2');
});

async function asyncFunction() {
console.log('Async Function Start');
await null;
console.log('Async Function End');
}

asyncFunction();

console.log('End');

執行順序:

玩玩看

console.log('Start')會先被執行,再來碰到setTimeout會進到背景去執行,而console.log('Timeout')callback function不會馬上被執行,即便setTimeout是設定0秒,還是會被放到Macro Task等待被執行,接下來Promise.resolve()會被執行,它後面有帶.then,所以console.log('Promise 1')會被放到micro task等待執行,再來則是執行asyncFunction()console.log('Async Function Start') 以及 await null,ㄈ在這裡碰到await 不管await執行的function是否為異步其後面的程式碼都會被包起來放到micro task等待被執行,接下來則執行 console.log('End');

到此synchronize的 code 已經沒有需要被放到 call stack,接下來檢查micro task,開始執行,console.log('Promise 1') 而後又有.then,這個console.log('Promise 2') callback function會被放到micro task中,再來執行console.log('Async Function End'),接下來因為console.log('Promise 2')還在micro task,所以會先被執行,最後才是執行macro task的console.log('Timeout');

順序
‘Start’
‘Async Function Start’
‘End’
‘Promise 1’
‘Async Function End’
‘Promise 2’
‘Timeout’

以上就是Event loop的執行流程。

參考資料:

--- 到底拉 The End ---