初學者指南:深入了解 JavaScript 中的 Event Loop(事件循環)
更新日期: 2024 年 11 月 21 日
本文為 JS 底層運作邏輯系列文,第一篇
- 初學者指南:深入了解 JavaScript 中的 Event Loop(事件循環) 👈 所在位置
- 初學者指南:深入了解 JavaScript 的 Call Stack(呼叫堆疊)
- 初學者指南:深入了解 JavaScript 的執行環境(Execution Context)
- 初學者指南:深入了解 JavaScript 的建立期與執行期
- 初學者指南:深入了解 JavaScript 中函式與變數的建立期與執行期差異
在學習 JavaScript 時,Event Loop(事件循環)是一個關鍵的概念。
它是 JavaScript 如何處理非同步操作(如 setTimeout
、Promise
、事件監聽器等)的核心機制。
對於新手來說,理解 Event Loop 有助於掌握 JavaScript 的非阻塞行為,以及如何正確處理異步程式。
本文將深入介紹 JavaScript 中的 Event Loop,並提供簡單的範例,來幫助你理解其工作原理。
JavaScript 是單執行緒的
在深入探討 Event Loop 之前,我們需要了解一個重要的背景知識:JavaScript 是單執行緒的。
這意味著它一次只能執行一個任務。
如果程式中有一個長時間運行的任務,這個任務會阻塞其他程式碼的執行,直到這個任務完成。
然而,JavaScript 需要能夠同時處理多個任務,如網絡請求、計時器等,這就是為什麼我們需要 Event Loop 來管理非同步任務的執行。
什麼是 Event Loop?
Event Loop 是 JavaScript 用來處理非同步操作的機制。
它允許 JavaScript 在單執行緒的情況下,仍能夠進行非阻塞的運算,並確保異步任務能夠在適當的時機執行。
Event Loop 的工作流程可以簡單概述為:
- Call Stack(呼叫堆疊)負責執行同步任務。
- 當異步任務(如
setTimeout
或Promise
)完成後,回調函式會被放入任務佇列(Task Queue)。 - 當 Call Stack 清空後,Event Loop 將從 Task Queue 中取出回調函式,並將它們推入 Call Stack 進行執行。
Event Loop 的工作原理
Call Stack
Call Stack 是一個資料結構,用來存儲程式在執行過程中的函式調用。
當一個函式被調用時,它會被添加到 Call Stack 中,當函式執行完畢後,會被移出 Call Stack。
Web APIs
當 JavaScript 執行異步操作(如 setTimeout
或 DOM 事件
)時,這些操作會被推送到瀏覽器提供的 Web APIs(例如計時器或事件處理器)來處理,並不會立即放入 Call Stack。
Task Queue(任務佇列)
一旦異步操作完成,對應的回調函式會被放入 Task Queue(也稱作 Message Queue 或 Callback Queue)。
Task Queue 是一個先進先出的佇列,存放著等待執行的回調函式。
Event Loop
Event Loop 的主要作用是監控 Call Stack 和 Task Queue。
如果 Call Stack 是空的,Event Loop 會從 Task Queue 中取出一個回調函式,並將它推入 Call Stack 執行。
這樣,JavaScript 就能在同步任務之後,處理異步任務。
Event Loop 的範例
讓我們來看一個簡單的範例,演示 Event Loop 的工作原理:
console.log("開始");
setTimeout(function() {
console.log("這是一個延遲執行的訊息");
}, 2000);
console.log("結束");
輸出:
開始
結束
這是一個延遲執行的訊息
解析執行順序
- 第一步:
console.log("開始");
立即執行,並輸出開始
。 - 第二步:
setTimeout
被調用,這是一個異步操作,會將回調函式放入 Web API,計時器開始計時。 - 第三步:
console.log("結束");
立即執行,並輸出結束
。 - 第四步:2 秒後,
setTimeout
的計時完成,回調函式被放入 Task Queue。 - 第五步:Event Loop 檢查 Call Stack 是否為空,當 Call Stack 清空後,它會將 Task Queue 中的回調函式推入 Call Stack,並執行
console.log("這是一個延遲執行的訊息");
。
微任務(Microtask)與宏任務(Macrotask)
在 JavaScript 中,任務分為兩種類型:宏任務(Macrotask) 和 微任務(Microtask)。
這兩種類型的任務會有不同的執行順序,了解它們的區別有助於我們更好地理解 Event Loop。
宏任務(Macrotask)
宏任務是比較常見的任務類型,包含的操作有 setTimeout
、setInterval
、I/O 操作
以及 DOM 事件
。
微任務(Microtask)
微任務的優先級比宏任務高,這意味著微任務會在每次事件循環結束前執行。
微任務包括 Promise 的回調
、MutationObserver
等。
宏任務與微任務的範例
console.log("開始");
setTimeout(function() {
console.log("setTimeout 宏任務");
}, 0);
Promise.resolve().then(function() {
console.log("Promise 微任務");
});
console.log("結束");
輸出:
開始
結束
Promise 微任務
setTimeout 宏任務
解析:
- 同步任務 先執行:
console.log("開始")
和console.log("結束")
會立即執行。 - Promise 作為微任務,會在同步代碼執行完畢後,立即執行。
- setTimeout 作為宏任務,會等到所有微任務執行完畢後,才執行。
Event Loop 在非同步操作中的應用
setTimeout
setTimeout
是 JavaScript 中最常見的異步函式之一,它將回調函式放入 Task Queue,並在指定的時間後執行。
範例:
setTimeout(function() {
console.log("2 秒後的訊息");
}, 2000);
這段程式碼會將回調函式放入 Task Queue,2 秒後回調會被 Event Loop 推入 Call Stack 執行。
Promise
Promise
是 JavaScript 中用來處理異步操作的一個強大工具,當 Promise
的狀態發生變化時,它的回調函式會被放入微任務佇列,並優先於宏任務執行。
範例:
Promise.resolve().then(function() {
console.log("這是 Promise 的回調");
});
這段程式碼中的回調會被放入微任務佇列,在所有同步程式碼執行完畢後立即執行。
面試常見問題與答案
Event Loop 是 JavaScript 中一個經常被提及的概念,很多面試題目會涉及它的工作原理。
以下是一些常見的面試問題及其答案:
Event Loop 是 JavaScript 中的一個機制,它允許 JavaScript 處理非同步操作。
它的主要作用是監控 Call Stack(呼叫堆疊)和 Task Queue(任務佇列),當 Call Stack 清空時,Event Loop 會從 Task Queue 中取出回調函式,並將它們推入 Call Stack 進行執行。
宏任務和微任務是 JavaScript 任務的兩種類型。
宏任務包括 setTimeout
、setInterval
、DOM 事件等,而微任務包括 Promise
回調和 MutationObserver
。
微任務的執行優先級高於宏任務,也就是說,微任務會在每次事件循環結束之前被處理。
Promise
回調會比 setTimeout
更早執行,因為 Promise
回調是微任務,而 setTimeout
是宏任務。根據 Event Loop 的運行順序,微任務會在宏任務之前執行。
事件循環是當 Call Stack 清空時發生的,這時 Event Loop 會檢查 Task Queue(或 Microtask Queue),並將回調函式或微任務推入 Call Stack 進行執行。
這個過程持續進行,以確保同步和異步代碼能夠正確執行。
JavaScript 是單執行緒的,這意味著它一次只能執行一個任務。
為了處理多任務,它依賴於 Event Loop 來管理非同步操作。當一個任務完成後,JavaScript 會將後續的回調函式放入 Task Queue,並通過 Event Loop 來控制它們的執行順序,從而實現非阻塞的運行。
理解 Event Loop 的重要性
順序控制
理解 Event Loop 有助於掌握 JavaScript 的程式執行順序,特別是在處理非同步操作時。
這可以幫助你避免錯誤地使用 setTimeout
或 Promise
,並確保程式在正確的時機執行。
錯誤排查
很多 JavaScript 程式的錯誤是由於誤解 Event Loop 的行為引起的。
當你理解了 Event Loop 的運作方式,將更容易診斷並解決因非同步操作錯誤而導致的問題。
結語
Event Loop 是 JavaScript 非同步編程的核心,它允許單執行緒的 JavaScript 進行多任務處理,保證了異步任務的正確執行。
通過這篇文章,您應該已經了解到 Event Loop 的基本工作原理、任務類型的區別,以及它在處理異步操作中的應用。
理解 Event Loop 是掌握 JavaScript 非同步程式設計的關鍵,希望本文能幫助你,更好地理解並應用這一概念。