Rick's DevNotes
筆記關於我作品集
筆記類別
  • 全部
  • DockerDocker
  • NetworkNetwork
  • RxJSRxJS
  • NginxNginx
  • TypeScriptTypeScript
  • Data_Structure_And_AlgorithmData Structure And Algorithm
  • JavaScriptJavaScript
  • PostgreSQLPostgreSQL
  • ReactReact
  • GitGit

© 2026 Rick's DevNotes. All rights reserved.

# Queue# Event Loop# Execution Stack# Runtime# Execution Context# Asynchronous

建立時間:2020/09/10

JavaScript 筆記:Execution Stack / Context 與 Event Loop

說到 JavaScript 的非同步,我剛開始學習時常常被搞得一頭霧水。明明 setTimeout 設定為 0 毫秒,為什麼還是會比 Promise 晚執行?為什麼有時候程式的執行順序跟想像中完全不一樣?

在深入學習 JavaScript 的過程中,我發現這些「奇怪」的現象背後,其實有一套很有邏輯的運作機制。也因此整理了這篇筆記,經過消化之後紀錄我在學習過程中對以下概念的理解:

  • 執行環境與執行堆疊:搞懂 JavaScript 怎麼管理函式調用
  • JavaScript Runtime:瞭解除了 JS 引擎還有哪些重要角色
  • Event Loop 機制:理解同步與非同步任務協作的觀念
  • Task Queue vs Micro Task Queue:釐清不同異步任務的執行優先級

執行環境 (Execution Context, EC) 與執行堆疊 (Execution Stack, ES)

在 JavaScript 執行時,每個任務(job)都會有自己的「執行環境」(Execution Context, EC)。JavaScript 會用一個「堆疊」(Execution Stack, ES)來管理這些執行環境。當有任務要執行時,它的執行環境就會被加到堆疊上。每個任務執行時,系統都會建立一個執行環境,裡面包含:

  1. 變數:var、let、const、函式、arguments 等。
  2. 範疇鏈 (Scope Chain):決定變數的可見範圍。
  3. This:函式執行時的上下文。

執行環境僅在任務被呼叫時,才會推入執行堆疊(遵循 LIFO 原則)。

範例

在 first() 呼叫過程中,堆疊會依序存放 Global、First 與 Second 的執行環境,並在任務完成後逐一彈出。

  1. Global 執行環境 儲存背景資訊,例如 myName="Rick"、first 及 second 的程式碼。
  2. First 執行環境 儲存函式內的資料,例如 a=1、b=未定義(呼叫 second 函式前)、c=未定義(計算前)。
  3. Second 執行環境 儲存 arguments=[2,3] 及 d=4 的資訊。

執行環境 (EC) 的堆疊方式

執行環境在執行堆疊中的堆疊順序依照程式執行的順序進行。當任務執行完畢後,其執行環境會被移出堆疊。以下範例展示了此過程:

Execution Stack

執行堆疊遵循 LIFO(Last In, First Out)原則。


JavaScript Runtime

JavaScript 程式碼的執行需要 JavaScript 引擎的支援。現代 JavaScript 引擎(如 Chrome 的 V8)會將程式碼解析、編譯並執行,過程中使用執行堆疊 (Execution Stack) 來管理函式調用的順序與執行環境。

除了 JavaScript 引擎,完整的 JavaScript Runtime 還包括:

  1. Web APIs:提供與 DOM、計時器函式及網路請求等功能
  2. Callback Queue:儲存等待執行的回調函式
  3. Event Loop:協調同步與非同步任務的執行

執行流程

以下範例說明 Runtime 的運作流程:

  1. JavaScript 引擎處理程式碼時採單線程方式,依序執行。
  2. 當觸發事件(如 onClick)時,事件的 callback function 會進入 Callback Queue。
  3. 當執行堆疊中的任務全數執行完畢後,事件迴圈 (Event Loop) 會將 callback function 推入執行堆疊中執行。
Event Loop

NOTE: DOM 操作、計時器函式等功能並非 JavaScript 的內建能力,而是由瀏覽器提供的 Web APIs 支援。


Event Loop 與非同步運作機制

Event Loop 是一種機制,用於協調執行堆疊(Execution Stack / Call Stack)、Web APIs 和任務佇列(Callback Queue)之間的工作流程。它確保非同步操作得以有序執行。JavaScript 為單執行緒語言,因此需要 Event Loop 來協調同步與非同步任務。其主要組成為:

  1. 執行堆疊(Call Stack):執行同步程式碼。
  2. Web APIs:瀏覽器提供的功能,如 setTimeout、DOM 操作、HTTP 請求。
  3. 任務佇列(Callback Queue):
    • Task Queue:包含 setTimeout、setInterval、HTTP 請求等。
    • Micro Task Queue:包含 Promise.then()、MutationObserver 等。

Task Queue vs Micro Task Queue 分類

Task Queue(宏任務佇列)

屬於 Task Queue 的非同步操作包括:

  • setTimeout() / setInterval()
  • setImmediate() (Node.js)
  • I/O 操作
  • UI 渲染事件
  • 使用者互動事件(click、scroll 等)
  • HTTP 請求回調

Micro Task Queue(微任務佇列)

屬於 Micro Task Queue 的非同步操作包括:

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

執行優先級規則

Micro Task Queue 的執行優先於 Task Queue,具體規則為:

  1. 每次 Event Loop 循環:

    • 執行一個 Task Queue 中的任務
    • 立即執行所有 Micro Task Queue 中的任務
    • 重複上述過程
  2. 優先級順序:

    • 同步程式碼 > Micro Task Queue > Task Queue

Micro Task 在執行時會先清空整個佇列,之後才會繼續執行下一個 Task。這意味著,如果 Micro Task 的數量過多,Task Queue 中的任務就會被延遲執行。


Event Loop 的工作流程

  1. 執行同步程式碼 → 在 Call Stack 內完成。
  2. 將非同步任務交給 Web APIs → 例如 setTimeout。
  3. 完成後推入 Callback Queue → 依照任務類型放入 Task Queue 或 Micro Task Queue。
  4. Event Loop 檢查 Stack 狀態 → 當堆疊清空後,先處理 Micro Task Queue,再處理 Task Queue。

範例

輸出順序:

進階範例:Micro Task 清空佇列的特性

輸出順序:

關鍵觀察:

  • 所有同步程式碼先執行 (1: Start, 7: End)
  • 所有 Micro Task 必須完全清空才會執行第一個 Task
  • 即使在 Micro Task 執行過程中新增的 Micro Task (5: Micro Task 2) 也會在同一輪中執行
  • Task Queue 中的任務 (2: Task 1, 3: Task 2) 被延遲到最後執行

參考資料

  • Jonas's JavaScript Course
  • Lydia Hallie