この記事では、ブラウザーとNode.jsがこの方法で EventLoop を設計している理由を詳しく説明します。皆さんのお役に立てれば幸いです。
イベント ループは JavaScript の基本的な概念です。面接でも必ず聞かれ、日常生活でもよく話題になります。しかし、なぜイベント ループがあるのか考えたことはありますか?イベントループとなぜこのようなデザインになっているのか? 毛織物?
今日はその理由を探っていきます。
JavaScript は、DOM 操作を含む Web ページ インタラクション ロジックの実装に使用されます。複数のスレッドが同時に動作する場合、同期と相互排他が行われます。処理は簡略化するためにシングルスレッドで設計されていますが、シングルスレッドの場合、タイミング ロジックやネットワーク リクエストが発生するとブロックされます。どうやってするの?
スケジューリング ロジックのレイヤーを追加できます。 JS コードをタスクにカプセル化してタスク キューに入れるだけで、メイン スレッドがタスクをフェッチして実行し続けます。
タスクが実行されるたびに、新しい呼び出しスタックが作成されます。
このうち、タイマーやネットワークリクエストは実際には別スレッドで実行され、実行後はタスクキューにタスクが置かれ、メインスレッドに継続を指示して実行されます。
これらの非同期タスクは他のスレッドで実行され、タスクキューを通じてメインスレッドに通知されるイベントメカニズムであるため、このループはイベントループと呼ばれます。 。
他のスレッドで実行されるこれらの非同期タスクには、タイマー (setTimeout、setInterval)、UI レンダリング、ネットワーク リクエスト (XHR またはフェッチ) が含まれます。
しかし、現状のイベントループには大きな問題があり、優先度の概念がなく、順番に実行されるだけで、優先度の高いタスクがあると実行が間に合わないのです。したがって、キュージャンプメカニズムを設計する必要があります。
あとは高優先度のタスクキューを作成し、通常のタスクを実行した後、高優先度のタスクをすべて実行し、その後に通常のタスクを実行するだけです。
#キュージャンプ機構により、高品質なタスクをタイムリーに実行できます。
これは現在のブラウザーのイベント ループです。
通常のタスクをMacroTask(マクロタスク)、高品質なタスクをMicroTask(マイクロタスク)と呼びます。
マクロ タスクには、setTimeout、setInterval、requestAnimationFrame、Ajax、フェッチ、スクリプト タグ コードが含まれます。
マイクロタスクには、Promise.then、MutationObserver、Object.observe が含まれます。
マクロタスクとミクロタスクの分割をどのように理解すればよいでしょうか?
タイマーとネットワーク リクエストは、他のスレッドの実行が終了した後にメイン スレッドに通知する一般的な非同期ロジックであるため、すべてマクロ タスクです。
3 種類の高品質なタスクも理解しやすいです。MutationObserver と Object.observe は両方とも、オブジェクトの変更を監視します。変更は非常に瞬時です。すぐに応答する必要があります。そうしないと、再び変更される可能性があります。はい、 Promise は非同期プロセスを整理しており、非同期エンドで then を呼び出すことも非常に優れています。
これはブラウザーのイベント ループの設計です:ループ メカニズムとタスク キューは、非同期をサポートし、メイン スレッドをブロックするロジック実行の問題を解決するように設計されています。MicroTask のキュー ジャンプ メカニズムqueue は、最適なタスクをできるだけ早く実行するという高い問題を解決するように設計されています。
しかし、その後、JS の実行環境はブラウザだけでなく、Node.js もこれらの問題を解決する必要がありましたが、イベント ループの設計はより詳細になりました。
Nodeは、タイマー、IO、ネットワークなどの非同期ロジックもサポートする新しい JS 実行環境です。当然のことながら、リクエストはイベント ループを使用して実行することもできます。
ただし、ブラウザのイベント ループはブラウザ向けに設計されており、高性能サーバーの場合はまだ少し荒い設計です。
どこが荒れていますか?
ブラウザのイベント ループは、優先度の 2 つのレベルにのみ分割されます。1 つはマクロ タスク、もう 1 つはマイクロ タスクです。ただし、マクロ タスク間にはそれ以上の優先順位はなく、ミクロ タスク間にもそれ以上の優先順位はありません。
Node.js タスク マクロ タスクにも優先順位があります。たとえば、タイマーのロジックは IO のロジックよりも高い優先順位を持ちます。これは、時間に関しては、早ければ早いほど正確であるためです。処理ロジックの優先度は非常に低くなります。これは、処理ロジックが閉じられていない場合、メモリなどのより多くのリソースを占有し、影響がほとんどないためです。
したがって、マクロ タスク キューは、タイマー、保留中、ポーリング、チェック、クローズの 5 つの優先順位レベルに分割されました。
次の 5 つのマクロ タスクについて説明します:
タイマー コールバック: 時間に関しては、実行が早ければ早いほど正確になるため、これが最も優先され、理解しやすいです。
保留中のコールバック: ネットワーク、IO、その他の例外を処理するときのコールバック。一部の *niux システムはエラーが報告されるのを待機するため、エラーを処理する必要があります。
ポーリング コールバック: IO データとネットワーク接続の処理。これはサーバーが主に処理するものです。
Check Callback: setImmediateを実行するためのコールバックで、IO実行直後にコールバックできるのが特徴です。
Close Callback: リソースを閉じるためのコールバック。遅延実行は影響を与えず、優先度が最も低くなります。
したがって、Node.js のイベント ループは次のように実行されます:
特別な注意を払う必要があるもう 1 つの違いがあります:
Node.js のイベント ループは、一度に 1 つのマクロ タスクを実行してからすべてのマイクロ タスクを実行するブラウザとは異なり、一定数のタイマー マクロ タスクを実行してから、すべてのマイクロ タスクを実行します。 , その後、一定数の保留中のマクロ タスクを実行し、すべてのマイクロ タスクを実行します。残りのポーリング、チェック、およびクローズのマクロ タスクにも同じことが当てはまります。(訂正: これはノード 11 より前のケースでした。ノード 11 以降は、各マクロ タスクがすべてのマイクロ タスクを実行するように変更されました)
これはなぜですか?
実際には、優先度に応じて理解するのが簡単です。
ブラウザ上のマクロタスクの優先度が 1 であると仮定すると、マクロタスクは順番に、つまり 1 つずつ実行されます。マクロタスク、すべて マイクロタスク、次にマクロタスク、そしてすべてのマイクロタスク。
Node.js のマクロ タスクにも優先順位があるため、Node.js のイベント ループは、毎回現在の優先順位ですべてのタスクを割り当てます。が終了したら、マイクロ タスクを実行し、次に優先度の高いマクロ タスクを実行します。
つまり、一定数のタイマー マクロ タスク、次にすべてのマイクロ タスク、次に一定数の保留中のコールバック マクロ タスク、そしてすべてのマイクロ タスクです。
なぜ一定の金額と言われるのでしょうか?
あるステージのマクロタスクが多すぎると次のステージが実行されなくなるため、上限があり、残った次のイベントループは実行され続けます。
優先順位のあるマクロ タスクに加えて、マイクロ タスクにも優先順位が付けられます。追加の高優先マイクロ タスク process.nextTick があり、通常のすべてのマイクロ タスクよりも前に実行されます。
したがって、Node.js イベント ループの完全なプロセスは次のようになります:
Node.js では、マクロ タスクに優先順位が付けられています。高い順に、タイマー、保留中、ポーリング、チェック、クローズです。また、マイクロ タスクにも優先順位が付けられています。 nextTick マイクロタスクとその他のマイクロタスクに分かれています。実行プロセスは、まず現在の優先度の一定数のマクロタスクを実行し (残りは次のサイクルに残します)、次に process.nextTick のマイクロタスクを実行し、次に通常のマイクロタスクを実行し、次に次の優先度の一定数を実行します。 .マクロタスクの数。 。このサイクルは続きます。 Node.js の内部ロジックには Idle/Prepare ステージもあるので、心配する必要はありません。
ブラウザのイベント ループで一度に 1 つのマクロ タスクを実行する方法を変更しました。これにより、優先度の高いマクロ タスクをより早く実行できるようになりますが、次のステージが実行されないことを防ぐための上限も設定されます。
もう 1 つ特に注意すべき点があります。それは、ポーリング ステージです。ポーリング ステージまで実行され、ポーリング キューが空で、実行するタスクがないことが判明した場合タイマー キューとチェック キューで実行されるとブロックされます。アイドル状態ではなく、ここで IO イベントを待ちます。この設計になっているのは、サーバーが主に IO を処理するためであり、ここでブロックすることで、より早く IO に応答できるからです。
完全な Node.js イベント ループは次のとおりです:
ブラウザのイベント ループと比較します:
2 つの JS 実行環境のイベント ループの全体的な設計アイデアは似ていますが、Node.js のイベント ループにはマクロ タスクとマイクロ タスクがより細かく分割されており、これも理解しやすい点が異なります。結局のところ、Node.js の環境はブラウザの環境とは異なり、さらに重要なことに、サーバーのパフォーマンス要件も高くなります。
JavaScript は、Web ページの対話ロジックを記述するために初めて使用されました。複数のスレッドが同時に DOM を変更するという同期の問題を回避するために、シングルスレッドのブロッキング問題を解決するには、ループ ループとタスク キューというスケジューリング ロジックの層が追加され、ブロッキング ロジックは他のスレッドに配置されて実行されるため、非同期操作がサポートされます。次に、優先度の高いタスクのスケジューリングをサポートするために、マイクロタスク キューが導入されました。これはブラウザのイベント ループ メカニズムであり、一度に 1 つのマクロ タスクを実行し、その後すべてのマイクロ タスクを実行します。
Node.js も JS 実行環境です。非同期をサポートしたい場合は、イベント ループも使用する必要があります。ただし、サーバー環境はより複雑で、より高いパフォーマンス要件があるため、Node .js にはマクロ タスクとマイクロ タスクの両方があります。より詳細な優先度分類を作成しました:
Node.js は、タイマー、保留、ポーリング、チェック、クローズという 5 種類のマクロ タスクに分かれています。マイクロタスクは、process.nextTick マイクロタスクとその他のマイクロタスクの 2 種類に分類されます。
Node.js のイベント ループ プロセスは、現在のステージで一定数のマクロ タスクを実行し (残りは次のループで実行されます)、その後すべてのマイクロ タスクを実行します。タイマー、保留中、アイドル/準備、ポーリング、チェック、クローズの 6 つのステージがあります。(訂正: これはノード 11 より前のケースでした。ノード 11 以降は、すべてのマイクロ タスクを実行するように各マクロ タスクに変更されました)
Idle/Prepare ステージは Node.js によって内部的に使用されます。だから心配しないでください。
ポーリング ステージには特別な注意を払う必要があります。ポーリング キューが空で、タイマーとチェック キューも空の場合、ここでブロックされ、コールバックがあるまで IO を待機します。タイマーとチェックキュー。ループを再度継続します。
イベント ループは、非同期とタスクの優先順位をサポートするために JS によって設計された一連のスケジューリング ロジックです。ブラウザや Node.js などの環境ごとに異なる設計になっています (主にタスクの優先順位の分割) (粒度が異なる)、Node.js はより複雑な環境に直面し、より高いパフォーマンス要件があるため、イベント ループの設計はより複雑になります。
ノード関連の知識の詳細については、nodejs チュートリアルを参照してください。 !
以上がブラウザーと Node.js が EventLoop をこのように設計した理由を発見してください。の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。