Node.js はノンブロッキング、非同期の性質で知られており、イベント ループがこの動作の中心にあります。これにより、メインスレッドがブロックされずに維持されるため、複数の操作が互いの終了を待たずに効率的に実行できるようになります。この記事では、イベント ループがどのように機能するかを調査し、その 6 つのフェーズに分けて、イベント ループのブロックを防ぐ戦略について説明します。
Node.js のイベント ループにより非同期処理が可能になり、メイン スレッドのブロックが回避されます。 6 つのフェーズで動作します:
イベント ループは、非同期操作の処理を担当するメカニズムです。 I/O やタイマーなどの操作が完了するたびに、イベント ループはその操作のコールバックをいつ実行するかを決定します。この設計により、Node.js はメインスレッドをブロックすることなく複数のリクエストを処理できるようになり、アプリケーションの高いパフォーマンスが保証されます。
イベント ループは周期的に動作し、6 つの異なるフェーズを通過します。各フェーズには特定の目的があり、コールバックはそれに応じて実行されます。
1.タイマーフェーズ
このフェーズでは、setTimeout と setInterval によってスケジュールされたコールバックを実行します。指定された遅延時間が経過すると、関連するコールバックがここで実行されます。
例:
setTimeout(() => { console.log('Executed after 1 second.'); }, 1000); console.log('Timer scheduled.');
出力:
Timer scheduled. Executed after 1 second.
遅延が 1000 ミリ秒 であっても、setTimeout は現在のイベント ループ ティックが完了した後に実行されます。
setInterval の例
let count = 0; const intervalId = setInterval(() => { console.log(`Interval executed: ${++count}`); if (count === 3) clearInterval(intervalId); }, 500);
2.コールバック保留フェーズ
このフェーズでは、イベント ループは前のサイクルから延期された I/O コールバックを処理します。これらのコールバックは、エラーとノンブロッキング I/O 操作を処理します。
例:
const fs = require('fs'); fs.readFile('file.txt', (err, data) => { if (err) console.error(err); else console.log(data.toString()); });
出力:
Read operation scheduled. File content:<contents of example.txt>
3.アイドル、準備フェーズ
このフェーズは、次のポーリング ラウンドに向けてシステムを準備するために Node.js によって内部的に使用されます。このフェーズと直接対話することはありませんが、内部ポーリングの設定などのタスクに焦点を当てることで、このフェーズに関連するいくつかの動作をシミュレートできます。
TCP サーバー設定の例 (準備状態)
const net = require('net'); const server = net.createServer((socket) => { socket.end('Connection closed.'); }); server.listen(8080, () => { console.log('Server listening on port 8080.'); });
準備フェーズでは、このサーバーを初期化します。準備が完了すると、受信接続を待つポーリング フェーズに移行します。
4.ポーリングフェーズ
ポーリングフェーズ中、イベント ループは新しい I/O イベントを待機し、関連するコールバックを実行します。保留中のイベントがない場合は、新しいイベントが発生するかタイマーの実行準備が整うまで、このフェーズに留まります。
setTimeout(() => { console.log('Executed after 1 second.'); }, 1000); console.log('Timer scheduled.');
ここで、サーバーは HTTP リクエストを待つポーリングフェーズに入ります。リクエストが到着すると、そのコールバックが実行され、レスポンスが送信されます。
5.チェックフェーズ
check フェーズでは、setImmediate でスケジュールされたコールバックが実行されます。これらのコールバックは、保留中の I/O 操作があるかどうかに関係なく、ポーリング フェーズの後に実行されます。
例:
Timer scheduled. Executed after 1 second.
出力:
let count = 0; const intervalId = setInterval(() => { console.log(`Interval executed: ${++count}`); if (count === 3) clearInterval(intervalId); }, 500);
6.コールバックを閉じるフェーズ
このフェーズではクリーンアップ操作を処理します。たとえば、socket.on('close') など、ネットワーク接続の終了に関連付けられたコールバックはここで実行されます。
const fs = require('fs'); fs.readFile('file.txt', (err, data) => { if (err) console.error(err); else console.log(data.toString()); });
出力:
Read operation scheduled. File content:<contents of example.txt>
クライアントが切断されると、socket.on('close') コールバックがクローズ コールバック フェーズで実行されます。
イベント ループは非同期操作を効率的に管理するように設計されていますが、ループをブロックするとパフォーマンスが低下する可能性があります。メインスレッドが負荷の高い計算や同期操作でスタックすると、他のコールバックが実行できなくなります。これにより遅延が発生し、アプリケーションが応答しなくなる可能性があります。
メインスレッドで CPU を大量に使用するタスク (大規模な計算など) を実行すると、イベント ループがブロックされます。ワーカー スレッドを使用してブロックを防ぐ方法は次のとおりです。
イベントループのブロック例
const net = require('net'); const server = net.createServer((socket) => { socket.end('Connection closed.'); }); server.listen(8080, () => { console.log('Server listening on port 8080.'); });
出力:
const http = require('http'); const server = http.createServer((req, res) => { res.end('Hello from server!'); }); server.listen(3000, () => { console.log('Server running on http://localhost:3000'); });
この例では、5 秒のブロック期間中は何も実行できないため、アプリケーションは応答しなくなります。
解決策: ワーカー スレッドの使用
setImmediate(() => { console.log('Executed in check phase.'); }); setTimeout(() => { console.log('Executed in timers phase.'); }, 0); console.log('Main code executed.');
出力:
Main code executed. Executed in check phase. Executed in timers phase.
ここでは、ブロッキング計算が別のスレッドで実行され、イベント ループが他のタスクを処理できるようになります。
Node.js は、画像処理、暗号化、または 複雑な計算などのタスクを処理するための ワーカー スレッド モジュールを提供します。これにより、負荷の高い操作を並行して実行し、イベント ループから作業をオフロードできます。
ワーカースレッドの例:
setTimeout(() => { console.log('Executed after 1 second.'); }, 1000); console.log('Timer scheduled.');
非同期関数または setImmediate を使用して、大きなタスクをより小さなノンブロッキング操作に分割します。
例:
Timer scheduled. Executed after 1 second.
イベント ループは Node.js のコア コンポーネントであり、非同期操作を効率的に管理する役割を果たします。 6 つのフェーズ (タイマー、保留中のコールバック、アイドルと準備、ポーリング、チェック、、クローズ コールバック) を理解することで、開発者はスムーズに実行されるノンブロッキング コードを作成できます。ただし、大量の計算によるイベント ループのブロックを避けることが重要です。 ワーカー スレッド などのツールを活用すると、アプリケーションの高速性と応答性が確保されます。イベント ループをマスターすると、スケーラブルでパフォーマンスの高い Node.js アプリケーションを構築できるようになります。
以上がNode.js のイベント ループ: 非同期操作の管理の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。