Event Loop for Node
理解が難しいため理解した内容を少しずつ追加していく
Event Loop とは
Javascriptはシングルスレッドなのにも関わらず、ノンブロッキングI/O(非同期処理)を実行するための仕組み
シングルスレッドはCPUの中で処理を1つずつ実行するような仕組み。マルチスレッドというものもあり、そっちはCPUの中で複数の処理を並列して実行できる仕組み。
ブラウザのEvent Loopとは異なる
同期タスクと非同期タスクがある
Event EmitterはEvent Loopのタスクに含まれない
Event Loopはtaskを同時に実行できないため、queueに入れられ逐次実行される
EventLoopは非同期処理をキューに入れて実行を後に回すだけなので同時に実行されるわけではない
code:tasks.ts
// asynchronous
setTimeout(() => console.log(1));
setImmediate(() => console.log(2));
process.nextTick(() => console.log(3));
Promise.resolve().then(() => console.log(4));
// synchronous
console.log(5);
// execution result
// > 5
// > 3
// > 4
// > 1
// > 2
tasks.tsのような結果になるのはなぜか
1. Event Loopの初期化後、Event Loopの開始前に以下の処理が実行される
タイマースケジュールの設定
process.nextTick()などの実行
同期タスクの実行
非同期APIの呼び出し
2. nextTickQueueが実行される
nextTickQueueはprocess.nextTick()を使用して登録されたcallbackを持つ
全ての非同期タスクの中で最速
3. nextTickQueueのqueueが空になるとmicroTaskQueueが実行される
Promiseオブジェクトのcallbackがここに帰属する
4. Timers Phase
setTimeoutやsetTimeIntervalのcallbackが実行される
timerが切れた場合は登録された順に実行される(FIFO)
OSのスケジューリングや他のコールバックの実行により遅延が発生する可能性があり、Node.jsではコールバックの実行する正確なタイミングや順序付けは保証されない
指定された時間のできるだけ近い時間で呼び出される
5. Pending Callbacks Phase
pending_queueに存在するtask(未実行のtask)が実行される
完了/ エラーのI/O操作に関するcallbackが実行される
6. Idle, Prepare Phase
7. Poll Phase
このフェーズは、サーバの応答、まだ返されていないI/Oイベントを待機するために使用されるポーリング時間
新しいソケットコネクトやファイルの読み込みなどの新しいI/Oイベントを取得し、実行
I / Oをブロックしてポーリングする時間を計算
キュー内のイベントを処理
queueが空でスケジューリングされている場合、イベントループはこのフェーズを終了し、次のcheckフェーズへ進みスケジュールされたスクリプトを実行する
queueが空でスケジュールされていない場合、イベントループはコールバックがキューへ追加されるのを待ち実行する
8. Check Phase
setImmediateで登録されたcallbackが実行される
timerフェーズのものとは異なり、専用のフェーズがあるため、必ず実行が保証される
pollフェーズで実行されていたコールバック内にsetImmediateが存在すれば、setTimeoutよりも先に呼ばれることが保証される
9. Close Callbacks Phase
すべての close イベントのコールバックが処理される(e.g. readable.on('close', () => {}))
もし、キューに処理するものがなければ、ループが終了する
存在すれば、timerフェーズへ遷移する
参考