Node.js - イベントループ: 非同期JavaScriptの魔法を解き明かす
こんにちは、未来のプログラミング魔术師たち!今日は、Node.jsの心臓部であるイベントループに興味深い旅に出かけましょう。これまで一度もコードを書いたことがない人も心配しないでください;この魅力的な世界を案内するあなたのフレンドリーなガイドとして私はここにいます。このチュートリアルの終わりまでに、Node.jsが同時に多くのことを行うことができる仕組みを理解するでしょう。あなたが宿題をしながら、Netflixを見て、友達にメッセージを送るのと同じように!
イベントループとは?
忙しいレストランの厨房のシェフを想像してください。複数の料理を同時に調理し、タイマーが鳴り、注文が入ります。それらを全部をバーンしないで、お客様を待たせないようにどのように管理しますか?それがまさにNode.jsのイベントループがやっていることです!
イベントループは、常に注意を必要とするものをチェックし、すべてがスムーズに進むようにするマスター・シェフのようです。JavaScriptがシングルスレッドであるにもかかわらず、Node.jsが非ブロッキングI/O操作を行うことができる秘密のソースです。
主要概念
さらに深く掘り下げる前に、いくつかの主要概念を理解しましょう:
- シングルスレッド: JavaScriptは単一のスレッドで動作しており、一度に一つのことをしかできない。
- ノンブロッキング: Node.jsは、一つの操作が終了するのを待たずに複数の操作を処理することができる。
- 非同期: タスクは今始めて、後で終了させることができ、その間他のコードを実行することができる。
イベントループの仕組み
イベントループを消化しやすいステップに分解してみましょう:
- コールスタック内の同期コードを実行
- タイマー(setTimeout、setInterval)をチェック
- 残りのI/O操作をチェック
- setImmediateのコールバックを実行
- 'close'イベントを処理
それでは、いくつかのコード例でこれを見てみましょう!
例1: 同期と非同期コード
console.log("First");
setTimeout(() => {
console.log("Second");
}, 0);
console.log("Third");
出力はどうなると思いますか?それを分解してみましょう:
- "First"は即座にログされます。
- setTimeoutに遭遇しますが、待機せずにタイマーを設定し続行します。
- "Third"はログされます。
- イベントループが完了したタイマーをチェックし、コールバックを実行して"Second"をログします。
出力:
First
Third
Second
驚きましたか?これはNode.jsがメインスレッドをブロックすることなく非同期操作をどのように処理するかを示しています。
例2: 複数のタイマー
setTimeout(() => console.log("Timer 1"), 0);
setTimeout(() => console.log("Timer 2"), 0);
setTimeout(() => console.log("Timer 3"), 0);
console.log("Hello from the main thread!");
この例では、複数のタイマーを0ミリ秒の遅延で設定しています。しかし、イベントループはメインスレッドが終了した後にそれらを処理します。
出力:
Hello from the main thread!
Timer 1
Timer 2
Timer 3
イベントループのフェーズ
イベントループの動作を見てきたので、そのフェーズをより詳細に探ってみましょう:
1. タイマーフェーズ
このフェーズでは、setTimeout()とsetInterval()によってスケジュールされたコールバックを実行します。
setTimeout(() => console.log("I'm a timer!"), 100);
setInterval(() => console.log("I repeat every 1 second"), 1000);
2. 残りのコールバックフェーズ
このフェーズでは、次のループイテレーションに遅延されたI/Oコールバックを実行します。
3. イディル、準備フェーズ
内部使用のみ。ここには何も見るものはありません!
4. ポールフェーズ
新しいI/Oイベントを取得し、I/O関連のコールバックを実行します。
const fs = require('fs');
fs.readFile('example.txt', (err, data) => {
if (err) throw err;
console.log(data);
});
5. チェックフェーズ
setImmediate()のコールバックがここで呼び出されます。
setImmediate(() => console.log("I'm immediate!"));
6. クローズコールバックフェーズ
いくつかのクローズコールバック、例えばsocket.on('close', ...)などがここで処理されます。
すべてを合わせて
イベントループのさまざまな側面を利用したより複雑な例を作成してみましょう:
const fs = require('fs');
console.log("Start");
setTimeout(() => console.log("Timeout 1"), 0);
setImmediate(() => console.log("Immediate 1"));
fs.readFile('example.txt', (err, data) => {
console.log("File read complete");
setTimeout(() => console.log("Timeout 2"), 0);
setImmediate(() => console.log("Immediate 2"));
});
console.log("End");
実行順序はあなたを驚かすかもしれません:
- "Start"と"End"は即座にログされます。
- 最初のsetTimeoutとsetImmediateがキューに追加されます。
- ファイル読み取り操作が開始されます。
- イベントループがサイクルを開始します:
- 最初のsetTimeoutコールバックが実行されます。
- 最初のsetImmediateコールバックが実行されます。
- ファイル読み取りが完了したときのコールバックが実行されます。
- ファイル読み取りコールバック内で、別のsetTimeoutとsetImmediateがキューに追加されます。
- 第二のsetImmediateが第二のsetTimeoutよりも前に実行されます。
イベントループに関連する一般的なメソッド
以下は、Node.jsでのイベントループに関連する一般的なメソッドの表です:
メソッド | 説明 |
---|---|
setTimeout(callback, delay) | 指定された遅延ミリ秒後コールバックを実行 |
setInterval(callback, interval) | 指定されたインターバルミリ秒ごとにコールバックを繰り返し実行 |
setImmediate(callback) | 次のイベントループイテレーションでコールバックを実行 |
process.nextTick(callback) | 現在の操作が完了した後にコールバックを「次のティックキュー」に追加 |
結論
おめでとうございます!あなたはNode.jsとそのイベントループの興味深い世界への第一歩を踏み出しました。非同期プログラミングをマスターするには練習が必要です。すぐに理解できない場合でもあきらめず、実験を続けると、すぐにプロのようにノンブロッキングコードを書けるようになるでしょう!
このまとめとして、楽しいアナロジーを提供します:イベントループを回転する merry-go-round と考えてください。さまざまなタスク(タイマー、I/O操作、即時コールバック)は子どものように乗りたいと思っています。イベントループは回転し続け、特定の順序でタスクを拾い上げて降ろし、誰もがターンを得ることを確保します。
codingを続け、好奇心を持ち続け、忘れないでください - Node.jsの世界では、忍耐はただの美徳ではなく、コールバックなのです!
Credits: Image by storyset