イベントループ(ノードバージョン)


最近編集者が忙しくてノードのイベントループを更新する時間がありませんでした。今日やっと更新する時間ができました。このバージョンは編集者版の神様です。この記事を読んだら皆さんの靴も強くなると思います!

ノードのマクロとマイクロタスク


1.ノードもマクロタスクとマイクロタスクに分かれる
2.ノードマクロはステージを考慮する

  • タイマー (タイマー: このステージの実行は setTimeout() および setInterval() スケジュール コールバック関数で行われます)
  • 保留中のコールバック (保留中のコールバック: 次のループ反復まで実行が遅延される I/O コールバック)
  • アイドル、準備 (システム内部使用のみ)
  • ポーリング (新しい I/O イベントを取得します。I/O 関連のコールバックを実行します (ほとんどの場合、クローズされたコールバック、タイマーと setImmediate() によってスケジュールされたコールバックを除く))、残りのノードはここでブロックされます。例: fs .readFile())
  • check (ここで setImmediate() コールバック関数が実行されます)
  • close コールバック (socket.on('close', …) などの一部のクローズされたコールバック関数)

3.ノードマイクロタスクステージ

  • 次のティックキュー
  • マイクロタスクキュー

実行シーケンスと用語の説明


ノードには6段階のマクロタスクを持たせることができ、各段階のイベントがクリアされる(または上限に達する)ごとに、マイクロタスクの順番でマイクロタスクが実行されます。

各ステージの用語は次のように説明されています。
1. タイマー、これは非常に単純です。これはタイマーです。
2. 保留中のコールバック。このフェーズでは、いくつかのシステム コールバック操作が実行されます。たとえば、TCP 接続を確立するときに、TCP ソケットは次のメッセージを受け取ります。このエラーは一部の liunx オペレーティング システムで報告され、このシステムのコールバックは保留中のコールバック、または次のイベント ループで実行する必要がある I/O コールバック操作に置かれます。(あまり気にしないでください)
3.アイドル、準備、いくつかの内部イベント。(ノードの最下層) (ユーザーはあまり気にする必要はありません)
4. 投票、上記の説明に従ってください。ここでは、名前が示すように、I/O 概念、IO (入力と出力) について説明します。 、入力と出力はIOです。ディスク、ネットワーク、マウス、キーボードなどはすべて IO としてカウントされ、通常 IO と呼ばれるもののほとんどは、ディスクやネットワーク上のデータ操作を指します。
ディスクの場合、IO=読み取りおよび書き込み、ネットワークの場合、IO=送受信。
5. 上記の概念に従って、実際には setImmediate であることを確認します。
6.コンセプトに従ってコールバックを閉じる

7.nextTickQueue、実際には process.nextTick
8.microTaskQueue、promise.then などの他のマイクロタスク


ここでは、ポーリングの4 番目と 6 番目のポイントに焦点を当てます
。ポーリングは、新しい I/O イベントを検出し、I/O 関連のコールバックを実行します。ここでのコールバックは、コールバック、タイマー、setImmediate を除くほぼすべてのコールバック イベントを閉じることを指します。

ポーリングは主に 2 つのことを処理します。つまり、I/O のポーリング、ブロック時間の計算、およびポーリング キュー内のイベントの処理です。

ポーリング キューが空でない場合、イベント ループはキュー内のコールバックを走査し、キューが消費されるかコールバック数の制限に達するまで、それらを 1 つずつ同期的に実行します。

キュー内のコールバックは 1 つずつ同期して実行されるため、ブロッキングが発生する可能性があります。

ポーリング キューが空の場合、コード内で setImmediate が呼び出されると、すぐに次のチェック フェーズにジャンプし、setImmediate でコールバックを実行します。

close コールバックの最後のフェーズ
では、close イベントでコールバックを処理します。たとえば、ソケットが突然閉じられると、close イベントがトリガーされ、関連するコールバックが呼び出されます。

開発者は基本的に気にする


開発者が注意すべき最も基本的なことは、上記の 1、4、5、7、および 8 です。上記の実行順序に従って出力を確認できます。以下は皆さんへの質問です。今回は、ノード 12.13.0バージョンが使用されます。

console.log('1');
new Promise(function(resolve) {
  console.log('10');
  resolve();
}).then(function() {
  console.log('11');
});

async function async1() {
    console.log('2');
    await async2();
    console.log('3');
}
async function async2() {
    console.log('4');
}

async1();

process.nextTick(function() {
    console.log('5');
})

setTimeout(function() {
    console.log('6');
    process.nextTick(function() {
        console.log('7');
    })
    new Promise(function(resolve) {
        console.log('8');
        resolve();
    }).then(function() {
        console.log('9')
    })
})

console.log('12');

私はゴージャスなディバイダーです
私は
ゴージャスなディバイダーです 私は
ゴージャスなディバイダーです 私はゴージャスなディバイダーです
私はゴージャスなディバイダーです 私はゴージャスなディバイダーです 私はゴージャスなディバイダー
です 私はゴージャスなディバイダーです
私はゴージャスなディバイダーです


ゴージャスなディバイダー
ですゴージャスなディバイダー 私はゴージャスなディバイダー
私はゴージャスなディバイダー
私はゴージャスな
ディバイダー
私はゴージャスなディバイダー
私はゴージャスなディバイダー

答えが発表されました

1
10
2
4
12
5
11
3
6
8
7
9

非常に簡単ですか? 実際には、ブラウザをベースに process.nextTick が追加されます。

console.log('1');
new Promise(function(resolve) {
  console.log('10');
  resolve();
}).then(function() {
  console.log('11');
});


async function async1() {
    console.log('2');
    await async2();
    console.log('3');
}
async function async2() {
    console.log('4');
}

async1();

process.nextTick(function() {
    console.log('5');
})

setImmediate(function() {
    console.log('13');
    process.nextTick(function() {
        console.log('14');
    })
    new Promise(function(resolve) {
        console.log('15');
        resolve();
    }).then(function() {
        console.log('16')
    })
})

setTimeout(function() {
    console.log('6');
    process.nextTick(function() {
        console.log('7');
    })
    new Promise(function(resolve) {
        console.log('8');
        resolve();
    }).then(function() {
        console.log('9')
    })
})

console.log('12');

私はゴージャスなディバイダーです
私は
ゴージャスなディバイダーです 私は
ゴージャスなディバイダーです 私はゴージャスなディバイダーです
私はゴージャスなディバイダーです 私はゴージャスなディバイダーです 私はゴージャスなディバイダー
です 私はゴージャスなディバイダーです
私はゴージャスなディバイダーです


ゴージャスなディバイダー
ですゴージャスなディバイダー 私はゴージャスなディバイダー
私はゴージャスなディバイダー
私はゴージャスな
ディバイダー
私はゴージャスなディバイダー
私はゴージャスなディバイダー

答えが発表されました

1
10
2
4
12
5
11
3
6
8
7
9
13
15
14
16

setTimeout の前に setImmediate が呼び出されるのは、ノードマクロがタイマーを先に実行してからチェックを実行すると考えているためです。理解できない場合は、上記の内容を読んでください。

ここで注意すべき点があります。タイマー setTimeout と setImmediate も実行され、マイクロタスクをクリアしてから、次のタイマーが実行されます。これはブラウザーと一貫しています。

最後に、別の質問を見てみましょう。第 4 段階を追加してみましょう。

const fs = require('fs')
fs.readFile('./name.txt','utf8',()=>{
    setTimeout(()=>{
        console.log('timeout')
    })
    setImmediate(()=>{
        console.log('setImmediate')
    });
})

私はゴージャスなディバイダーです
私は
ゴージャスなディバイダーです 私は
ゴージャスなディバイダーです 私はゴージャスなディバイダーです
私はゴージャスなディバイダーです 私はゴージャスなディバイダーです 私はゴージャスなディバイダー
です 私はゴージャスなディバイダーです
私はゴージャスなディバイダーです


ゴージャスなディバイダー
ですゴージャスなディバイダー 私はゴージャスなディバイダー
私はゴージャスなディバイダー
私はゴージャスな
ディバイダー
私はゴージャスなディバイダー
私はゴージャスなディバイダー

答えが発表されました

setImmediate
timeout

最初に第 4 ステージ (fs) にいて、次に第 5 ステージ (チェック) に進み、その後第 1 ステージ (タイマー) に戻るためです。

要約する


1. ノード内のタイマーと setImmediate は実際にはブラウザと一致しています。これらを 1 つにまとめて実行し、次のノードを先入れ先出しで実行します。違いはノードが段階に分かれていることと実行順序です。 2. ノード
マイクロ タスクは 2 つのステップに分割され、マイクロタスクの処理は nextTick の優先度が高くなります。

マクロ思考とミクロタスクのスケーリング


編集者が情報を拝見しましたが、実際にはマクロタスクやマイクロタスクがたくさんありますが、以下のようなものはあまり使われないので、拡張として考えてみましょう。

マクロタスクマクロタスク:

(イベントキュー内の各イベントはマクロタスクです)

優先順位: メイン コード ブロック > setImmediate > MessageChannel > setTimeout / setInterval ほとんどのブラウザーは、ユーザー エクスペリエンスを向上させてユーザーにフィードバックを提供したいため、DOM イベント コールバックを優先し、次にネットワーク IO 操作コールバック、次に UIrender、その後の順序になります。実際、ブラウザごとにパフォーマンスも異なるため、ここではあまり説明しません。)

例: setImmediate で指定されたコールバック関数は、常に setTimeout の前に配置されます。

マイクロタスクには次のものが含まれます。

優先順位: process.nextTick > Promise > MutationObserver

process.nextTick が常に Promise.then より大きいことにさらに注意を払う必要があります。

終わり


この記事を読んで、node のイベント ループを理解して、以前のブラウザのイベント ループと合わせて読んでいただけたでしょうか。おめでとうございます、子供靴さん、レベルが上がりました。

おすすめ

転載: blog.csdn.net/zjscy666/article/details/117335725