iT邦幫忙

2022 iThome 鐵人賽

DAY 3
0

上一篇文章提到了單執行緒非同步執行機制的好處,在於可以善用個別執行緒的資源,不在耗費時間的 IO 請求上浪費時間等待。

而幫助 Node 做到這件事的就是 Event Loop 這個機制!

Call Stack

Call stack 是 Node 用來記錄目前要執行什麼函式的一個資料結構,是個 LIFO(後進先出) 的構造。
而 event loop 會不斷輪詢查看裡面是否有需要執行的 function,只要有,就會優先去執行。

這部分官網文件圖解特別清楚,所以在這邊不另外畫圖詳解,請點進去看一下流程圖,會很清楚他的概念:
Call Stack


Event Queue

                                           ┌───────────────────────────┐
                                    ┌─────>│       NextTick Queue      │
                                    │      └─────────────┬─────────────┘
                                    │      ┌─────────────┴─────────────┐
                                    │      │       MicroTask Queue     │
                                    │      └─────────────┬─────────────┘
                                    │      ┌─────────────┴─────────────┐
                                    │      │           timers          │
                                    │      └─────────────┬─────────────┘
                                    │      ┌─────────────┴─────────────┐
┌───────────────────────────┐       │      │     pending callbacks     │
│         Call Stack        │<──────┤      └─────────────┬─────────────┘
└───────────────────────────┘       │      ┌─────────────┴─────────────┐
                                    │      │       idle, prepare       │
                                    │      └─────────────┬─────────────┘
                                    │      ┌─────────────┴─────────────┐
                                    │      │           poll            │
                                    │      └─────────────┬─────────────┘
                                    │      ┌─────────────┴─────────────┐
                                    │      │           check           │
                                    │      └─────────────┬─────────────┘
                                    │      ┌─────────────┴─────────────┐
                                    └──────┤      close callbacks      │
                                           └───────────────────────────┘

當 Call stack 為空時,event loop 會去檢查 event queue,將裡面的任務放進 call stack 內做執行。
而 event queue 其實是由很多個 queue 所組成的,而 queue 是一種 LILO(後進後出)的構造。而這些 queue 內正是放著 node 的非同步的任務們。

event queue 官網有詳解且有圖示可參考:
Event Loop

大概歸納一下,Event Loop 在執行時的執行優先序大概是這樣:

if (Call Stack 非空) {
  // 執行 Call Stack pop 出的內容
} else if (NextTick Queue 非空) { // process.nextTick 傳的 callback 會被放進這個 queue
  // 取出 NextTick Queue 任務進 call stack
} else if (MicroTask Queue 非空) { // promise 非同步要執行的 callback 會被放進這邊
  // 取出 MicroTask Queue 任務進 call stack
} else if (Timers 非空) { // setTimeout 或 setInterval 的計時觸發時,callback 會被放進這邊
  // 取出 Timers 任務進 call stack
} else if (Pending Callback 非空) { // 為某些系統操作(例如 TCP 錯誤)執行的 callback
  // 取出 Pending Callback 任務進 call stack
} else if (Idle, Prepare 非空) { // node 內部使用,可忽略
  // 取出 Idle, Prepare 任務進 call stack
} else if (Polling 非空) {
  // 取出 Polling 任務進 call stack
} else if (Check 非空) { // setImmediate 的 callback 會被放進這邊
  // 取出 Check 任務進 call stack
} else if (Close Callback 非空) { // 處理連線或是 handle 關閉的 callback (例如 socket.destroy())
  // 取出 Close Callback 任務進 call stack
}

如果上面不太理解的,可以參考這篇,裡面有程式範例可以比對,該筆者鐵人賽還有出書,我覺得也寫的蠻不錯的。
全端工程師生存筆記

還有這個影片也可以參考:
The Complete Node js: The Node js Event Loop


小結論

  • 當我們在 terminal 輸入 node main.js 時,event loop 會先依序去執行 call stack 的內容 (main.js 的程式會被依行並且根據函式執行的位置被放進去,執行完的內容則會被 pop 出 stack)
  • 當 call stack 已空,若還有 pending 的 timer 或是 io 任務,event loop 則會持續監聽 event queue 們。反之若無,則程式會 exit。
  • 在執行過程中任何的異步語法或是 io 指令,在執行完後,callback 或其 handle 會被加入 event queue 內,等待 event loop 將他拉進 call stack 內執行。
  • 異步事件有其優先序,為上述排序,但基本上為 nextTick > promise (async) > timer > setImmediate

單執行緒學會了多重詠唱

透過 event loop,node 在 單執行緒上不用再擔心阻塞問題,並且可以有效率的同時處理許多 IO 請求。
這不是跟魔法一樣嗎?


明天我們將繼續了解 node 如何在單執行緒的限制下,善用多核 CPU 的優勢。


上一篇
[Day 2] 單執行緒的魔法
下一篇
[Day 4] 用 Cluster 創建多個 Process
系列文
身為 Node.js 開發者,可以知道一下的事9
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言