NodeJS - Event loop

Node JS 並非 「Single thread environment」

  • 而是會有一個thread來負責處理主程式以及Event loop
  • 主程式
    • 也就是循序執行我們所打的每一行 JS code
    • 先處理完主程式, 才會處理 event loop 收到的 callback
1
2
3
4
5
6
7
8
9
setTimeout(function() {
console.log("timer callback!");
}, 3000);

let start = Date.now();
while(Date.now() - start < 5000) {
console.log('5s later');
}
console.log('main program complete!');

result :

1
2
3
5s later
main program complete!
timer callback! // 3秒後其實就完成了, 但需要等 main program 的5秒 while loop 完成

Event loop

  • 跑完一次 loop 稱作一個 tick
  • 每個 Phase 有自己個 queue, 用來存放 callback function
    • 到該 Phase 時, queue 中的 callback 會依序執行
  • 當無等待的 timerI/O 事件 時, Event loop 結束
  • Phases in loop:

    • timer
      • 執行 setTimeout(), setInterval() 的 callback
    • I/O callback
      • 執行一些 I/O 完成後的 callback
        • e.g.
          • 從網路擷取資料
          • 寫檔案/讀檔案
          • 當 I/O 操作發生錯誤時
    • idle, prepare
      • 執行一些 libuv 內部的事情
    • poll
      • 輪詢特定 I/O
        • e.g. http listening
      • 當 Event loop 中其他 Phase 中所有 queue 中皆空, 且有 一個以上的輪詢 I/O 時, 會 block 在此 phase
        • e.g. http.listen(…)
        • 假設有其他 Phase 之非同步事件完成, 有 callback 加入了該 Phase 的 queue, 則會跳至該 Phase 去執行
    • check
      • 執行 setImmidiate() 之 callback
    • close
      • 執行 close 相關之 callback
        • e.g. file.close()
  • process.nextTick and microtasks

    • 其 callback queue 會立即加入 當前Phase 的 queue 的末端
      • 在主程式結束後也包含了這個 queue
    • 包含了:
      • process.nextTick() 的 callback
      • microtasks: Promise 的 reslove/reject
    • nextTick queue 會在 microtasks queue 前執行
  • setImmidiate() v.s. process.nextTick()

    • process.nextTick()
      • 會將 task queue 在當前 Phase 的後面
      • 遞迴 Call 時, 將導致遲遲無法進去下一個 Phase, 塞住整個 event loop
    • setImmidiate()
      • 會將 task queue 在 check phase
      • 當遞迴 Call 時, 會將 task queue 在下一次 iteration loop 的 check phase
        • 遞迴呼叫時, 不會 block 住 event loop .

參考文獻