Skip to main content

Event Loop

Event Loop 的工作原理

Event Loop 透過以下三個核心概念來管理異步執行:

1. 任務隊列管理

將異步任務分為兩個隊列:

  • Macro-task(宏任務):較大的任務單位
  • Micro-task(微任務):較小的任務單位

2. 循環執行機制

不斷檢查任務隊列,按優先級執行任務

3. 時間限制與瀏覽器優化

HTML5 規範中,setTimeout 的最小間隔時間被設定為 4ms

  • 即使設定延遲為 0ms,實際執行時間至少是 4ms
  • 避免過度頻繁的定時器調用,保護瀏覽器性能
  • 不同瀏覽器可能有不同的最小間隔時間實現

Macro-task vs Micro-task

Macro-task(宏任務)

定義:較大的任務單位,每次事件循環只執行一個 macro-task

常見的 macro-task

  • setTimeout / setInterval
  • setImmediate(Node.js)
  • I/O 操作
  • UI 渲染
  • 整體的 script 代碼

Micro-task(微任務)

定義:較小的任務單位,在每個 macro-task 執行完畢後,會清空所有 micro-task 隊列

常見的 micro-task

  • Promise.then() / Promise.catch() / Promise.finally()
  • process.nextTick()(Node.js)
  • queueMicrotask()
  • MutationObserver

執行順序

script 代碼 → Micro-task → Macro-task
caution

setImmediate() 是 Node.js 專有的 API,不屬於標準 JavaScript(即 ECMAScript 規範)。 它僅在 Node.js 環境中可用,瀏覽器環境(如 Chrome、Firefox)中不存在 setImmediate

實際案例解析

讓我們通過一個具體的例子來理解 Event Loop 的執行順序:

// macro-task
setImmediate(function () {
console.log(1);
});

// macro-task
setTimeout(function () {
console.log(2);
}, 0);

// micro-task
new Promise(function (resolve) {
console.log(3);
resolve();
console.log(4);
}).then(function () {
console.log(5);
});

console.log(6);

// micro-task
process.nextTick(function () {
console.log(7);
});

console.log(8);

// 輸出結果:3 4 6 8 7 5 2 1

執行步驟詳細解析

第一階段:同步代碼執行

// output: 3 4 6 8
  • console.log(3) - Promise 建構函數內的同步代碼
  • console.log(4) - Promise 建構函數內的同步代碼
  • console.log(6) - 同步代碼
  • console.log(8) - 同步代碼

第二階段:Micro-task 執行

// output: 7 5
  • process.nextTick() 優先級最高
  • Promise.then() 其次

第三階段:Macro-task 執行

// output: 2 1
  • setTimeoutsetImmediate 先執行

Event Loop 的優勢

Event Loop 確保了:

  • 非阻塞執行:長時間運行的任務不會凍結 UI
  • 響應性:用戶操作能夠及時響應
  • 效率:合理分配 CPU 資源給不同優先級的任務

實際應用場景

1. 避免阻塞主線程

// 不好的做法
function heavyTask() {
for (let i = 0; i < 1000000; i++) {
// 大量計算
}
}

// 好的做法
function heavyTaskAsync() {
return new Promise((resolve) => {
setTimeout(() => {
// 將重任務分解為小任務
resolve();
}, 0);
});
}

2. 優化用戶體驗

// 使用 micro-task 確保 UI 更新優先
function updateUI() {
Promise.resolve().then(() => {
// UI 更新邏輯
});
}

總結

Event Loop 是 JavaScript 異步編程的基石,理解它的工作原理對於:

  • 調試異步代碼問題
  • 優化應用性能
  • 避免常見的異步陷阱
  • 提升代碼質量

都有著重要的意義。

掌握 Event Loop 不僅能讓我們寫出更好的異步代碼,更能幫助我們理解 JavaScript 這個語言的設計哲學。

參考資料