今回はNode.jsのイベントループの詳しい説明と、Node.jsのイベントループを使用する際の注意点についてお届けします。以下は実際のケースですので見てみましょう。
Node.jsもシングルスレッドのイベントループですが、その動作メカニズムはブラウザ環境とは異なります。
下の模式図を見てください
上図によると、Node.jsの動作仕組みは以下の通りです。
(1) V8 エンジンは
JavaScript スクリプトを解析します。
(2) 解析されたコードは Node API を呼び出します。
(3) libuv ライブラリはノード API の実行を担当します。異なるタスクを異なるスレッドに割り当ててイベントループ(イベントループ)を形成し、タスクの実行結果を非同期でV8エンジンに返します。
(4) V8 エンジンは結果をユーザーに返します。
2 つのメソッド setTimeout と setInterval に加えて、Node.js は「Task
Queue」に関連する他の 2 つのメソッド、process.nextTick と setImmediate も提供します。これらは、「タスクキュー」についての理解を深めるのに役立ちます。
process.nextTick メソッドは、現在の「実行スタック」の終わり、つまり次のイベント ループ (メインスレッドが「タスク キュー」を読み取る) の前に、
コールバック関数 をトリガーできます。つまり、指定したタスクは常にすべての非同期タスクの前に発生します。 setImmediate メソッドは、現在の「タスク キュー」の末尾にイベントを追加します。つまり、指定したタスクは常に次のイベント ループで実行されます。これは setTimeout(fn, 0) とよく似ています。以下の例を参照してください (StackOverflow経由)。
process.nextTick(function A() {
console.log(1);
process.nextTick(function B(){console.log(2);});});setTimeout(function timeout() {
console.log('TIMEOUT FIRED');}, 0)// 1// 2// TIMEOUT FIRED
ログイン後にコピー
上記のコードでは、process.nextTick メソッドで指定されたコールバック関数は常に現在の「実行スタック」の最後でトリガーされるため、setTimeout で指定されたコールバック関数のタイムアウト前に関数 A が実行されるだけでなく、関数B もタイムアウトが発生する前に実行されます。これは、複数の process.nextTick ステートメントがある場合 (ネストされているかどうかに関係なく)、それらはすべて現在の「実行スタック」で実行されることを意味します。
次に、setImmediate を見てください。
setImmediate(function A() {
console.log(1);
setImmediate(function B(){console.log(2);});});setTimeout(function timeout() {
console.log('TIMEOUT FIRED');}, 0);
ログイン後にコピー
上記のコードでは、setImmediate と setTimeout(fn,0) のそれぞれに、次のイベント ループでトリガーされるコールバック関数 A とタイムアウトが追加されます。それでは、どのコールバック関数が最初に実行されるのでしょうか?答えは定かではありません。実行結果は、1--TIMEOUT FIRED--2 または TIMEOUT FIRED--1--2 となる場合があります。
混乱を招くのは、Node.js のドキュメントには、setImmediate で指定されたコールバック関数は常に setTimeout よりも前にランク付けされると記載されていることです。実際、これは再帰的に呼び出した場合にのみ発生します。
setImmediate(function (){
setImmediate(function A() {
console.log(1);
setImmediate(function B(){console.log(2);});
});
setTimeout(function timeout() {
console.log('TIMEOUT FIRED');
}, 0);});// 1// TIMEOUT FIRED// 2
ログイン後にコピー
上記のコードでは、setImmediate と setTimeout は setImmediate にカプセル化されており、その実行結果は常に 1--TIMEOUT FIRED--2 になります。このとき、関数 A はタイムアウト前にトリガーされる必要があります。 TIMEOUT FIRED の 2 番目の順位 (つまり、関数 B がタイムアウト後にトリガーされる) については、setImmediate が常にイベントを次のイベント ループのラウンドに登録するため、関数 A と timeout は同じループのラウンドで実行されます。関数 B はループの次のラウンドで実行されます。
ここから、process.nextTick と setImmediate の重要な違いがわかります。複数の process.nextTick ステートメントは常に現在の「実行スタック」で一度に実行されますが、複数の setImmediate では複数のループの実行が必要になる場合があります。実際、これが Node.js バージョン 10.0 で setImmediate メソッドを追加した理由です。そうしないと、次のような process.nextTick への再帰呼び出しが無限に行われ、メイン スレッドが「イベント キュー」をまったく読み取れなくなります。
process.nextTick(function foo() {
process.nextTick(foo);});
ログイン後にコピー
実際、ここで再帰的 process.nextTick を記述すると、Node.js は警告をスローし、それを setImmediate に変更するように求めます。
また、process.nextTickで指定したコールバック関数はこの「イベントループ」でトリガーされるのに対し、setImmediateで指定したコールバック関数は次の「イベントループ」でトリガーされるので、前者の方が必ず先に発生することがわかります。後者は実行効率も高い(「タスクキュー」を確認する必要がないため)。
この記事の事例を読んだ後は、この方法を習得したと思います。さらに興味深い情報については、php 中国語 Web サイトの他の関連記事に注目してください。
推奨読書:
JavaScriptのタイマーの詳細な説明
JavaScriptの実行メカニズムのイベントとコールバック関数の詳細な説明
ブラウザのマルチスレッドメカニズムの詳細な説明
js小さな問題
以上がNode.jsのイベントループの詳細説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。