本篇內容參考連結
[第十六週] JavaScript 進階:事件迴圈 Event Loop、Stack、Queue
[筆記] 理解 JavaScript 中的事件循環、堆疊、佇列和併發模式(Learn event loop, stack, queue, and concurrency mode of JavaScript in depth)
Philip Roberts | JSConf EU
在準備了解Event Loop前, 必須先回憶一下一些資料結構
由於JS Engine是單執行緒(Single thread), 只能執行同步(synchronous)事件, 並一次執行一個任務, 所以需要一個機制來處理非同步(asynchronous)的事件. 而這個機制的集中一個環節就叫做 Event Loop, 用來決定執行任務的順序.
是用來記錄程式目前執行到程式哪個部分, 當進入某一個函式, 便會把這個函式添加(push)到最上方, 直到函式return或結束則會從堆疊抽離(pop).
參考以下範例
function b() {
console.log("b finished");
}
function a() {
b();
console.log("a finished");
}
a();
結果會得到
b finished
a finished
當我們去執行的時候, 首先進入stack的是檔案中全域環境的程式(這裡稱作 main()), 如下
main()
接著往下執行, a() 首先被呼叫, 此時會把a()函式堆疊在上面, 並先進入a()函式做執行, 而在a()中, b()又被呼叫,又做了一次堆疊在最上面, 最後call stack 會長的如下
b();
a();
main();
之後跑到 console.log("b finished");
後, b()執行完畢, 並會從call stack中抽離
a();
main();
以此類推, a()執行完,也會從 call stack中抽離, 最後回到 main()中繼續逐行執行直到結束, 然後main()也抽離.
function a() {
a();
}
a();
當有非同步操作的時候, 像是setTimeout, 瀏覽器會先丟一個timer到Web API, 等時間到了後, 就會把timer中的 Callback function 丟進Callback Queue. 再藉由Event Loop的機制, 等待Call stack淨空後, 就會開始執行.
console.log("First");
setTimeout(function(){
console.log("Second");
},5000);
console.log("Third");
執行結果為
"First"
"Third"
"Second"
先來看call stack的變化, 首先會先放入 main(), 之後執行console.log("First");
. 接下來執行到setTimeout這個非同步函式, 瀏覽器丟一個timer到Web API, 在此同時 setTimeout 就算執行完了, 也從stack抽離, 並不會阻塞整個程式五秒, 所以下一個console.log("Third");
就會接著執行.
再來看callback queue, 當在Web API的timer時間到了後, 會把setTimeout的callback丟入callback queue. 等最後一個main()也抽離並淨空後,才會依序把callback丟回 call stack, 並開始執行 console.log("Second");
.
在一個一個了解架構後, 終於回到Event Loop, 其作用就是監控堆疊(call stack)和佇列(callback queue),當堆疊當中沒有執行項目的時候, 便把佇列中的內容拉到堆疊中去執行.
Event Loop 的監測順序
強調 : 第一優先永遠是call stack, 當call stack沒淨空時, 即使非同步的事件處理完, 還是需要等到call stack都執行完, callback queue才會被執行到