イベントループ
Node.js のシングルスレッドで非同期 I/O を実現する実行モデル
Node.js非同期
イベントループとは
イベントループは、Node.js のシングルスレッドで非同期 I/O を実現する実行モデルである。コールスタック、タスクキュー、マイクロタスクキューを巡回し、非同期コールバックを順番に実行する。
動作の流れ
イベントループはコールスタックが空になるたびに、まずマイクロタスクキュー (Promise の .then など) をすべて処理し、次にタスクキュー (setTimeout、I/O コールバック) から 1 つ取り出して実行する。このサイクルを繰り返すことで、シングルスレッドでも非同期処理を効率的にさばく。
1. コールスタックが空か確認
2. マイクロタスクキュー (Promise) を全て実行
3. タスクキュー (setTimeout, I/O) から 1 つ実行
4. 1 に戻る
┌─────────────────────────┐
│ コールスタック │ ← 同期コードを実行
└────────────┬────────────┘
↓
┌─────────────────────────┐
│ マイクロタスクキュー │ ← Promise.then, queueMicrotask
└────────────┬────────────┘
↓
┌─────────────────────────┐
│ タスクキュー │ ← setTimeout, setInterval, I/O
└─────────────────────────┘
実行順序
実行順序のコード例を示す。
console.log('1'); // 同期
setTimeout(() => console.log('2'), 0); // タスクキュー
Promise.resolve().then(() => console.log('3')); // マイクロタスク
console.log('4'); // 同期
// 出力: 1, 4, 3, 2
// 同期 → マイクロタスク → タスクキュー の順
Node.js のイベントループフェーズ
Node.js のイベントループフェーズを以下にまとめる。
| フェーズ | 処理内容 |
|---|---|
| timers | setTimeout, setInterval のコールバック |
| pending callbacks | I/O コールバック |
| poll | 新しい I/O イベントを取得 |
| check | setImmediate のコールバック |
| close callbacks | socket.on('close') 等 |
ブロッキングの問題
ブロッキングの問題のコード例を示す。
// ❌ イベントループをブロック (他のリクエストが処理できない)
function heavyComputation() {
for (let i = 0; i < 1e9; i++) { /* CPU 集約的な処理 */ }
}
// ✅ Worker Threads で別スレッドに逃がす
import { Worker } from 'worker_threads';
const worker = new Worker('./heavy.js');
シングルスレッド vs マルチスレッド
シングルスレッドとマルチスレッドの違いを以下にまとめる。
| 観点 | Node.js (イベントループ) | Java (スレッドプール) |
|---|---|---|
| I/O 処理 | 高速 (ノンブロッキング) | スレッドごとにブロック |
| CPU 処理 | 苦手 (シングルスレッド) | 得意 (マルチスレッド) |
| メモリ | 少ない | スレッドごとにスタック |
| 複雑さ | コールバック/Promise | デッドロック、競合 |
詳しくは関連書籍を参照。
この記事は役に立ちましたか?
関連用語
非同期プログラミング
I/O 待ちの間に他の処理を進め、システムのスループットを向上させるプログラミング手法
goroutine
Go の軽量スレッドで、数百万の並行処理を低コストで実現する
プロセスとスレッド
OS のプロセスとスレッドの違い、およびプログラムの並行実行モデル
スレッドプール
事前に生成したスレッドを再利用し、スレッド生成のオーバーヘッドを削減する並行処理パターン
並行処理
複数のタスクを論理的に同時に進行させるプログラミング手法で、システムのスループットと応答性を向上させる
Worker Threads
Node.js で CPU 集約的な処理をメインスレッドをブロックせずに別スレッドで実行する仕組み