學習筆記 - 究竟什麼是 Event Loop?

AC 2–2 學習能力驗收題組

RMO
3 min readJul 3, 2021

情境

你正在研究「倒數計時」的功能,在尋找適合 JavaScript 語法的過程中,認識了一個叫 setTimeout 的函式。你快速看過了 MDN 的範例以後,自己隨手做了一點嘗試:

setTimeout(function(){console.log('delay 0 sec')}, 0)
console.log('Hello!')

然後看到了這個結果:

你忽然就很想知道為什麼「延遲 0 秒」的函式明明寫在文件上方,應該先被執行,但在 console 印出的結果,他還是被排在第二順位。

你問某個強者助教這個問題,但剛好助教很忙,沒想太多,他直接丟了一支叫「What the heck is the event loop anyway?」,說這支影片是關於這個問題的經典演講:

Q: 究竟什麼是 Event Loop?

在回答這個問題之前,需要先了解JavaScript runtime的流程,接下來是以影片中提到的Google Chrome browser來解釋。

JavaScript runtime表示能夠執行JavaScript程式碼的環境,其中Google Chrome是以V8 engine來執行JavaScript程式碼,因此Google Chrome是JavaScript的其中一種runtime。

Picture is taken from the same video above
Picture is taken from the same video above

JavaScript 本身是單執行緒(single-threaded)的程式語言,依序處理程式碼,等到當前的程式碼執行完畢之後才會繼續處理後續的程式碼。

那麼JavaScript要怎麼處理非同步(asynchronous)任務呢?

Web APIs: 使JavaScript能夠處理非同步任務

  • DOM(document Object Model)
  • AJAX(XMLHttpRequest)
  • SetTimeOut
  • 其他

處理機制介紹

  • Call Stack:
    依序處理JavaScript程式碼。
  • Web APIs:
    當函式(function)被推進Call Stack之後,需要非同步處理的函式就會被送到Web APIs處理,讓Call Stack能夠不需等待,直接繼續處理後續的程式碼。
  • Task(Callback) Queue:
    當Web APIs內的函式處理完之後就會被送到Task Queue並照著先後順序排列。
  • Event Loop:
    隨時檢查Call Stack 是否已經清空(所有同步程式碼都執行完畢),確認清空狀態後才從Task Queue當中把排列好的任務一次一個送到Call Stack處理。

這邊簡單整理出流程

  1. 同步任務:
    Call Stack — 完成同步任務
  2. 非同步任務:
    Call Stack — Web APIs — Task(Callback) Queue — Event Loop(確認Call Stack清空) — Call Stack — 完成非同步任務

回到問題: 究竟什麼是 Event Loop?

我們有了答案:
當Call Stack處理完同步(synchronous)任務後會清空Call Stack,這時Event Loop 負責將Web APIs處理完成且送到Task Queue等待的非同步(asynchronous)任務回傳(一次一個)到Call Stack接續處理。

其他與JavaScript runtime相關的補充資訊

Call Stack

  • Call Stack是追蹤程式碼執行狀態的地方。
  • 在Call Stack裡的資料會被指向Memory Heap。
  • 遵照後進先出原則(LIFO)。

Stack Overflow

  • 當一個函式不斷執行自身這個函式時,會導致Call Stack被填滿以及溢出。
  • 當Call Stack溢出,關於Call Stack超過上限的錯誤訊息會出現。
  • 下方的程式碼示範一個函式不斷執行自身這個函式,造成Stack Overflow。
function inception() {
inception();
}
inception(); //this causes stack size exceeded error

Memory Heap

  • Memory Heap 是分配記憶體存放變數、函式及其他需要用到記憶體的資源的地方。

Garbage Collection

  • JavaScript 在資料使用完畢之後會自動釋放該資料所佔用的記憶體。
  • 處理memory leaks。
  • 自動控制Memory Heap。
  • 以Mark and Sweep 演算法來處理Garbage Collection程序。

資料超載時會發生Memory Leaks,例如:array裡面有無窮盡的資料,使browser停止運作。

let array = [];
for(let i=1; i>0; i++) {
array.push(i);
} // this creates an array with infinite data, which causes memory leaks.

JavaScript主要的記憶體管理概念是根據reachability(可到達性),具備"可到達性"的資料就能夠儲存在記憶體,一旦失去"可到達性",該記憶體內的資料就會被清除。

舉例:

// user has a reference to the object
let user = {
name: "John"
};

全域變數user對應參照一個{name: “John”}的物件,這個物件裡的name屬性有儲存一個值"John",這個值可以被取用,具備"可到達性",因此會留存在記憶體。

如果user這個變數的值在後續被覆寫,使參照位址消失。

user = null;

現在{name: “John”}這個物件不具備"可到達性",因沒有任何變數可以指向這個物件,Garbage Collector就會刪除這個資料並釋放記憶體。

--

--