ノードイベントループNode の基盤となる言語は C++ 言語であるlibuvです。これは、基盤となるオペレーティング システムを操作するために使用され、オペレーティング システム インターフェイスをカプセル化します。 Node のイベント ループも Node はオペレーティング システムを処理するため、イベント ループは比較的複雑で、独自の API がいくつかあります。 イベントループ図約束通り、写真も載せますので、皆さんをハラハラさせません。下の図を理解すれば、イベント ループを学習したことになります。 イベントループ図 イベントループ図 - 構造 皆様に概要をご理解いただくために、ディレクトリ構造図を以下に示します。 目次 次に、詳しくお話ししましょう。 メインスレッドメインスレッド 上の図では、いくつかのカラーブロックの意味は次のとおりです。
イベントループイベントループ 図中の灰色の円はオペレーティング システムに関連しており、この章の分析の焦点ではありません。黄色とオレンジ色の円、および中央のオレンジ色のボックスに特に注意してください。 イベント ループの各ラウンドを「サイクル」と呼びます。これは「ポーリング」または「ティック」とも呼ばれます。 サイクルは 6 つの段階を経ます。
今回は、上記の赤でマークされた 3 つの重要なポイントにのみ焦点を当てます。 仕組み
このうち、 タイマーキューの仕組みタイマーは本当の意味でのキューではなく、内部にタイマーを格納します。 チェックプロセス:各タイマーを順番に計算し、タイマーの開始から現在時刻までの時間がタイマー間隔パラメータ設定を満たしているかどうかを計算します(たとえば、1000msの場合、タイマーの開始から1分が経過したかどうかを計算します)。タイマー チェックに合格すると、そのコールバック関数が実行されます。 投票キューの仕組み
イベントフローの例タイムアウトを設定する(() => { console.log('オブジェクト'); }, 5000) コンソールにログ出力します。 上記コードのイベントフロー メインスレッドに入り、setTimeout() を実行します。コールバック関数は非同期タスクとして非同期キュータイマーに配置され、当面は実行されません。
この問題を理解するには、次のコードとプロセス分析を見てください。 setTimeout(関数t1() { コンソールログに'setTimeout'と入力します。 }, 5000) console.log('ノードのライフサイクル'); 定数 http = require('http') 定数サーバー = http.createServer(関数 h1() { console.log('リクエストコールバック'); }); サーバー.listen(8080) コード分析は次のとおりです。
6 つのキューにタスクがない場合、タスクはポーリング キューで待機します。以下のように表示されます。
無限ループ... イベント ループ フロー チャートを整理します。 注: 下の図の「タスクはありますか」という用語は、「このキューにタスクがありますか」という意味です。 イベントループプロセスの概要 典型的な例を使用してプロセスを検証してみましょう。 定数 startTime = 新しい Date(); setTimeout(関数f1() { console.log('setTimeout', 新しい Date(), 新しい Date() - startTime); }, 200) console.log('ノードライフサイクル', startTime); 定数 fs = require('fs') fs.readFile('./poll.js', 'utf-8', 関数 fsFunc(err, data) { const fsTime = 新しい日付() コンソールにログ出力します。 (新しいDate() - fsTime < 300) の間 { } console.log('無限ループの終了', new Date()); }); 3 回連続して実行し、次の結果を出力します。 実行プロセス分析:
十分に待った後、イベント ループに戻ります。 イベント ループは、他の非同期タスクがないことを確認し、スレッドを終了し、プログラム全体を終了します。 チェックフェーズチェックフェーズ(setImmediate を使用するコールバックは直接このキューに入ります) チェックキューの実際の仕組み 実際のキューには、実行されるコールバック関数のコレクションが含まれています。 [fn,fn]の形式に似ています。 したがって、setImmediate はタイマーの概念ではありません。 面接に行って Node が関係する場合、次のような質問に遭遇する可能性があります。setImmediate と setTimeout(0) のどちらが速いですか? setImmediate() と setTimeout(0)
要約すると、setTimeout でもタイマー スレッドを開始して計算オーバーヘッドを増やす必要があるため、setImmediate は setTimeout(0) よりも高速に動作します。 両者の効果は似ています。しかし、執行順序は不明である 次のコードを確認してください。 タイムアウトを設定する(() => { コンソールログに'setTimeout'と入力します。 }, 0); setImmediate(() => { コンソールにログ出力します。 }); 何度も繰り返し実行した結果、実行効果は次のようになります。 不確かな順序 複数回実行した場合、2 つの console.log ステートメントの順序は固定されていないことがわかります。 上記のコードでは、メインスレッドの実行中に setTimeout 関数が呼び出され、タイマースレッドがタイマータスクを追加します。 setImmediate 関数が呼び出されると、そのコールバック関数がすぐにチェック キューにプッシュされます。メインスレッドの実行が完了しました。 eventloop はタイマーとチェック キューにコンテンツがあることを判断すると、非同期ポーリングに入ります。 最初のケース: タイマーの時間が来たときに、残り 1 ミリ秒がない可能性があり、タイマー タスク間隔の条件が満たされないため、タイマーにコールバック関数がありません。チェックキューまで進みます。このとき、setImmediate のコールバック関数は長時間待機していたため、直接実行されます。次にイベントループがタイマー キューに到達すると、タイマーが満了し、setTimeout コールバック タスクが実行されます。したがって、順序は「setImmediate -> setTimeout」となります。 2 番目のケース: ただし、タイマー段階に達したときに 1 ミリ秒を超える可能性もあります。したがって、計算タイマー条件が満たされ、setTimeout コールバック関数が直接実行されます。次に、イベントループはチェック キューに移動して、setImmediate のコールバックを実行します。最終的な順序は「setTimeout -> setImmediate」です。 したがって、この 2 つの機能だけを比較する場合、2 つの実行順序の最終結果は、現在のコンピューターの動作環境と動作速度によって異なります。 2つの時間差の比較コード ------------------setTimeout テスト:------------------- i = 0 とします。 コンソールで時間を設定します。 関数テスト() { (i < 1000) の場合 { setTimeout(テスト、0) 私は++ } それ以外 { コンソールのtimeEnd('setTimeout'); } } テスト(); ------------------setImmediate テスト:------------------- i = 0 とします。 コンソールで時間を設定します。 関数テスト() { (i < 1000) の場合 { setImmediate(テスト) 私は++ } それ以外 { コンソールのtimeEnd()関数は、次のようになります。 } } テスト(); 実行して時間差を観察します。 setTimeoutとsetImmediateの間の時間差 setTimeoutはsetImmediateよりも時間がかかることがわかります。 これは、setTimeout がメイン コードの実行時間を消費するだけではないからです。また、タイマー キューには、タイマー スレッド内のスケジュールされた各タスクの計算時間があります。 ポーリングキューに関する面接の質問(タイマー、ポーリング、チェックの実行順序の調査)上記のイベント ループ図を理解していれば、次の質問は難しくないでしょう。 // 次のコードの実行順序について説明してください。どれが最初に印刷されるでしょうか? 定数 fs = require('fs') fs.readFile('./poll.js', () => { setTimeout(() => console.log('setTimeout'), 0) setImmediate(() => console.log('setImmediate')) }) 上記のコード ロジックが何回実行されても、setImmediate は常に最初に実行されます。 まずsetImmediateを実行する 各 fs 関数のコールバックがポーリング キューに配置されるためです。プログラムがポーリング キューに保持されている場合、コールバックが直ちに実行されます。 次へチェックと約束マクロタスクについて話した後は、ミクロタスクについて話しましょう。
次のティック式 プロセス.nextTick(() => {}) 約束の表現 Promise.resolve().then(() => {}) イベントループに参加するにはどうすればいいですか? イベント ループでは、各コールバックを実行する前に、nextTick と promise を順番にクリアします。 // まず次のコードの実行順序を検討してください setImmediate(() => { コンソールにログ出力します。 }); プロセス.nextTick(() => { console.log('nextTick 1'); プロセス.nextTick(() => { console.log('nextTick 2'); }) }) console.log('グローバル'); Promise.resolve().then(() => { console.log('約束1'); プロセス.nextTick(() => { console.log('promise の nextTick'); }) }) 最終注文:
2つの質問: 上記に基づいて、検討して解決すべき 2 つの質問があります。
上記の2つの質問については、以下のコードを参照してください。 タイムアウトを設定する(() => { コンソールログ('setTimeout 100'); タイムアウトを設定する(() => { コンソールログ('setTimeout 100 - 0'); プロセス.nextTick(() => { console.log('setTimeout 100 - 0 の nextTick'); }) }, 0) setImmediate(() => { console.log('setTimeout 100 で setImmediate を実行'); プロセス.nextTick(() => { console.log('nextTick in setImmediate in setTimeout 100'); }) }); プロセス.nextTick(() => { console.log('setTimeout100 内の nextTick'); }) Promise.resolve().then(() => { console.log('setTimeout100 の約束'); }) }, 100) 定数 fs = require('fs') fs.readFile('./1.poll.js', () => { console.log('ポーリング1'); プロセス.nextTick(() => { console.log('投票のnextTick ======'); }) }) タイムアウトを設定する(() => { コンソールログ('setTimeout 0'); プロセス.nextTick(() => { console.log('setTimeout 内の nextTick'); }) }, 0) タイムアウトを設定する(() => { コンソールログ('setTimeout 1'); Promise.resolve().then(() => { console.log('setTimeout1 の約束'); }) プロセス.nextTick(() => { console.log('setTimeout1 内の nextTick'); }) }, 1) setImmediate(() => { コンソールにログ出力します。 プロセス.nextTick(() => { console.log('setImmediate 内の nextTick'); }) }); プロセス.nextTick(() => { console.log('nextTick 1'); プロセス.nextTick(() => { console.log('nextTick 2'); }) }) console.log('グローバル ------'); Promise.resolve().then(() => { console.log('約束1'); プロセス.nextTick(() => { console.log('promise の nextTick'); }) }) /** 実行順序は以下のとおりです global ------ 次へ1をチェック 次へ2をチェック 約束1 次へ約束を守る setTimeout 0 // 問題の説明 1. 上記の nextTick と promise がないと、setTimeout と setImmediate の順序は不定です。これらがあれば、0 が必ず最初に開始されます。 // キューを実行する前に、nextTick と promise マイクロキューが最初にチェックされ、実行されることがわかります。nextTick in setTimeout タイムアウト1を設定する setTimeout1 の nextTick setTimeout1 の約束 即時設定 setImmediate の nextTick 投票 1 次へ投票にチェックを入れる ====== タイムアウト100を設定する setTimeout100 の nextTick setTimeout100 の約束 setTimeout 100 の setImmediate setImmediate の nextTick と setTimeout の 100 タイムアウト設定 100 - 0 setTimeout の nextTick 100 - 0 */ 上記のコードは同じ順序で複数回実行され、setTimeout と setImmediate の順序は変更されません。 実行順序と具体的な理由は次のとおりです。
拡張: setImmediate があるのに、なぜ nextTick と Promise が必要なのでしょうか? 最初に設計されたとき、setImmediate はマイクロキューとして機能しました (実際はそうではありません)。設計者は、poll が実行された直後に setImmediate が実行されることを期待しています (もちろん、現時点では実際にその通りです)。したがって、名前は そこで、真のマイクロキューのコンセプトである nextTick が登場しました。ただし、このとき、immediate の名前が取られるので、名前は nextTick (次のティック) になります。イベント ループ中、キューを実行する前に、キューが空かどうかがチェックされます。 2つ目はPromiseです。 面接の質問最後に、学習成果をテストするためのインタビューの質問です 非同期関数 async1() { console.log('非同期開始'); async2() を待機します。 console.log('非同期終了'); } 非同期関数 async2(){ コンソールログ('async2'); } console.log('スクリプトの開始'); タイムアウトを設定する(() => { コンソールログ('setTimeout 0'); }, 0) タイムアウトを設定する(() => { コンソールログ('setTimeout 3'); }, 3) setImmediate(() => { コンソールにログ出力します。 }) プロセス.nextTick(() => { コンソールにログ出力します。 }) 非同期1(); 新しいPromise((res) => { コンソールにログ出力します。 解像度(); コンソールにログ出力します。 }).then(() => { console.log('約束3'); }); console.log('スクリプト終了'); // 答えは次のとおりです // - // - // - // - // - // - // - // - // - // - // - // - /** スクリプト開始 非同期開始 非同期2 約束1 約束2 スクリプト終了 次のチェック 非同期終了 約束3 // 次の 3 つの操作は、コンピューターの計算速度を確認するためのものです。 最速(上記の同期コード + マイクロタスク + タイマー操作を実行するのに 0 ミリ秒未満): 即時設定 タイムアウト0を設定する タイムアウト3の設定 速度は中程度です(上記の同期コード + マイクロタスク + タイマー操作を実行するには 0 ~ 3 ミリ秒以上かかります)。 タイムアウト0を設定する 即時設定 タイムアウト3の設定 速度が遅い (上記の同期コード + マイクロタスク + タイマー操作の実行に 3 ミリ秒以上かかりました): タイムアウト0を設定する タイムアウト3の設定 即時設定 */ マインドマップノードライフサイクルのコアフェーズ Node イベントループの包括的な理解に関するこの記事はこれで終わりです。Node イベントループの詳細については、123WORDPRESS.COM の以前の記事を検索するか、次の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。 以下もご興味があるかもしれません:
|
<<: MySQL ファジークエリステートメントコレクション
>>: Windows 10 での MySQL 8.0.19 のインストールと設定のチュートリアル
以下のように表示されます。名前説明する文字可変(n)、varchar(n)長さ制限あり、可変長文字(...
タブ切り替えもプロジェクトではよく使われる技術です。一般的にタブ切り替えはjsやjqを使って実装され...
コンテナの応用はますます一般的になっていますが、大量のコンテナをどのように管理すればよいのでしょうか...
PS: 最近、nginx を詳細に紹介している <<High-Performance ...
目次1. プロトタイプとプロトタイプチェーンの平等関係を理解する2: プロトタイプとプロトタイプ チ...
<br />この Web ページ制作スキル チュートリアルは、Web サイトのアイコンを...
React Native は、現在人気のオープンソース JavaScript ライブラリ React...
昨日、1年間使用していた Alibaba Cloud サーバーを購入しました。システムは Linux...
序文: vue3.0の要素フレームワークを使用します。要素はvue2.0をサポートしており、vue3...
問題の説明長い間アカウントパスワードを入力して Zabbix にログインしていないため、管理者パスワ...
序文私が必要としているのは、構文の強調表示、関数プロンプト、自動行折り返し、およびコードの折りたたみ...
mysql使用中に接続数が超過していることが判明しました~~~~ [root@linux-node...
この記事では、Webオンラインチャットを実装するためのVueの具体的なコードを参考までに紹介します。...
ネットワークの高可用性を実現するには、複数のネットワーク カードを仮想ネットワーク カードにバインド...
1. 基本概念 //任意のコンテナを Flex レイアウトとして指定できます。 。箱{ ディスプレイ...