Event Loop
Event Loop 可以理解為整個 JavaScript 程式運行的核心機制。 因為 JavaScript 是**單執行緒(single-threaded)的語言,它同一時間只能做一件事。為了能有效處理大量操作(例如等待 API 回應、計時器、DOM 事件等),就必須透過非同步機制(asynchronous mechanism)**來執行程式碼,避免程式被「阻塞(block)」。
JavaScript 運行時的主要組件
Heap: 記憶體區域,用來儲存**物件(Object)**等大型資料。
Call Stack(呼叫堆疊): 負責記錄目前正在執行的函數。 採用**後進先出(LIFO, Last In First Out)**的結構:
當函數被呼叫時,會被**推入(push)**堆疊。
當函數執行完畢,會從堆疊彈出(pop)。
Task Queues(任務隊列): 當某些非同步操作完成時,對應的回呼函數(callback)會被排進這些隊列中,等待 Call Stack 清空後進入執行。
Macro Task Queue(巨集任務隊列) 例如:
setTimeout
、setInterval
、setImmediate
、Web API callback 等。Micro Task Queue(微任務隊列) 例如:
Promise.then/catch/finally
、MutationObserver
。 Microtasks 的優先度比 Macrotasks 更高。
運作流程(Event Loop 如何工作)
當程式開始執行時,同步程式碼會直接進入 Call Stack。
非同步操作(如
setTimeout
、fetch
等)會交給瀏覽器的 Web APIs,當這些操作完成後,對應的 callback 會被推到適當的 Task Queue 中。Event Loop 會不斷檢查:
當 Call Stack 為空時,會先從 Microtask Queue 拿任務執行。
Microtasks 全部清完後,才從 Macrotask Queue 拿下一個任務執行。
不斷重複以上步驟,直到所有任務都處理完畢。
補充:同步 vs 非同步
同步:一件事情做完,才能做下一件事。會直接在 Call Stack 中排隊,容易阻塞。
非同步:把事情交給外部處理(像是瀏覽器的 Web API),程式可以繼續做其他事,不會被卡住。
一個超簡單的小範例:
輸出順序會是:
解釋:
start
和end
是同步程式,直接進 Call Stack。Promise.then
是 Microtask,會排在同步之後馬上執行。setTimeout
是 Macrotask,即便時間設 0,還是要等到 Microtask 都清完後才執行。
Last updated