iT邦幫忙

2

[學習筆記] JavaScript - Event Loop (1) : 機制以及相關延伸

  • 分享至 

  • xImage
  •  

本篇內容參考連結
[第十六週] JavaScript 進階:事件迴圈 Event Loop、Stack、Queue
[筆記] 理解 JavaScript 中的事件循環、堆疊、佇列和併發模式(Learn event loop, stack, queue, and concurrency mode of JavaScript in depth)
Philip Roberts | JSConf EU

Event Loop

Data Structure

在準備了解Event Loop前, 必須先回憶一下一些資料結構

  1. Stack 堆疊 : 原則是 LIFO ("Last in, First Out"), 最後進去的先出來.
  2. Queue 佇列 : 原則是 FIFO ("First in, First Out"), 所謂先進先出.

Definition

由於JS Engine是單執行緒(Single thread), 只能執行同步(synchronous)事件, 並一次執行一個任務, 所以需要一個機制來處理非同步(asynchronous)的事件. 而這個機制的集中一個環節就叫做 Event Loop, 用來決定執行任務的順序.

Call Stack

是用來記錄程式目前執行到程式哪個部分, 當進入某一個函式, 便會把這個函式添加(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()也抽離.

Call Stack - 補充

  • Stack Overflow : 資源是有限的, 當程式一直堆疊的時候, 例如函式不斷呼叫自己呈現無限迴圈如下, 程式進入a()之後就一直不斷重複堆疊, 到最後會出現錯誤 (Maximum call stack size exceeded), 也就是 Stack Overflow.
function a() {
    a();
}

a();
  • Blocking : 當執行一段程式需要等待很長的時間, 像是網頁卡住的感覺, 就叫做阻塞 Blocking. 像是把非同步 ajax request 請求變成同步(Synchronous), 堆疊時就逼須等request拿到資料後, 並從stack中pop出去才可以往下執行.

Callback Queue

當有非同步操作的時候, 像是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

在一個一個了解架構後, 終於回到Event Loop, 其作用就是監控堆疊(call stack)和佇列(callback queue),當堆疊當中沒有執行項目的時候, 便把佇列中的內容拉到堆疊中去執行.

Event Loop 的監測順序

  1. 監控call stack
  2. 監控callback queue
    • 如果call stack為空, 且call queue有pending callback
    • 把callback queue裡的程式, 依序丟入call stack去執行
  3. 回到步驟一

強調 : 第一優先永遠是call stack, 當call stack沒淨空時, 即使非同步的事件處理完, 還是需要等到call stack都執行完, callback queue才會被執行到

Event Loop - 補充

  • setTimeout 0 : 雖然字面上看起來是不用等, 但實際上利用Event Loop的機制, 使它裡面的程式全部跑到callback queue, 並等到全部程式跑完才會依queue的順序執行
  • render engine : 為了創造更流暢的UI,應該避免把笨重和等待時間長久的Code放在Call Stack, 因為優先順序是Call Stack > Render Engine > Callback queue. 當都放在Call Stack時會造成Render Engine無法更新, 相反的放在callback queue,

Event Loop 延伸主題

  1. macrotask and microtask
  2. Job queue

圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言