web-dev-qa-db-ja.com

イベントループについて

私はそれについて考えており、これが私が思いついたものです:

次のようなコードがあるとしましょう:

console.clear();
console.log("a");
setTimeout(function(){console.log("b");},1000);
console.log("c");
setTimeout(function(){console.log("d");},0);

要求が到着し、JSエンジンが上記のコードの実行を段階的に開始します。最初の2つの呼び出しは同期呼び出しです。しかし、setTimeoutメソッドになると、非同期実行になります。しかし、JSはすぐにそこから戻り、Non-BlockingまたはAsyncと呼ばれる実行を続けます。そして、他の作業を続けます。

この実行の結果は次のとおりです。

a c d b

したがって、基本的に2番目のsetTimeoutが最初に終了し、そのコールバック関数は最初の関数よりも早く実行され、それは理にかなっています。

ここでは、シングルスレッドアプリケーションについて説明しています。 JS Engineはこれを実行し続け、最初のリクエストを終了しない限り、2番目のリクエストには進みません。しかし、良いことは、setTimeoutのようなブロック操作が解決するのを待たずに、新しい着信要求を受け入れるため、より高速になることです。

しかし、私の質問は次の項目について発生します。

#1:シングルスレッドのアプリケーションについて話している場合、JSエンジンはより多くのリクエストを受け入れて実行する一方で、どのメカニズムがsetTimeoutsを処理するかそれら?シングルスレッドは他のリクエストでどのように動作し続けますか? setTimeoutで機能するのは、他のリクエストが引き続き受信され、実行されます。

#2:より多くのリクエストが来て実行されている間にこれらのsetTimeout関数がバックグラウンドで実行される場合、非同期実行を実行するもの舞台裏? EventLoopと呼ばれるこのことは何ですか?

#3:しかし、メソッド全体をEventLoopに入れて、全体が実行され、コールバックメソッドが呼び出されるようにしないでください?これは、コールバック関数について話すときに理解するものです:

function downloadFile(filePath, callback)
{
  blah.downloadFile(filePath);
  callback();
}

しかし、この場合、JSエンジンは非同期関数であるかどうかをどのようにして知るので、コールバックをC#のEventLoop? Perhaps something like theasync`キーワードまたはJS Engineが実行するメソッドを示す何らかの属性に入れることができますは非同期メソッドであり、それに応じて処理する必要があります。

#4:しかし、 記事 は、物事がどのように機能するかについて私が推測していたこととはまったく反対です。

イベントループは、コールバック関数のキューです。非同期関数が実行されると、コールバック関数がキューにプッシュされます。 JavaScriptエンジンは、非同期関数の後のコードが実行されるまで、イベントループの処理を開始しません。

#5:ここに役立つ画像がありますが、画像の最初の説明は質問番号4で述べたものとまったく同じことを言っています。

enter image description here

それで、ここでの私の質問は、上記の項目についていくつかの説明を得ることですか?

122
Tarik

1:シングルスレッドのアプリケーションについて話している場合、JSエンジンがより多くの要求を受け入れて実行する間にsetTimeoutsを処理するのは何ですか?その単一のスレッドは他のリクエストで引き続き動作しませんか?その後、他のリクエストが来て実行され続ける間、誰がsetTimeoutの作業を続けます。

ノードプロセスには、プログラムのJavaScriptを実際に実行するスレッドが1つしかありません。ただし、ノード自体には、実際にはイベントループメカニズムの操作を処理する複数のスレッドがあり、これにはIOスレッドのプールと他のいくつかのスレッドが含まれます。重要なのは、これらのスレッドの数が、スレッドごとの同時実行モデルのように処理される同時接続の数に対応していないことです。

「setTimeoutsの実行」について、setTimeoutを呼び出すと、すべてのノードは基本的に、将来一度に実行される関数のデータ構造を更新します。基本的に、実行する必要があるもののキューの束があり、イベントループのすべての「ティック」が1つを選択し、キューから削除して実行します。

理解すべき重要なことは、ノードがほとんどの重荷をOSに依存していることです。したがって、着信ネットワーク要求は実際にOS自体によって追跡され、ノードがノードを処理する準備ができたら、システムコールを使用して、データを処理できるネットワーク要求をOSに要求するだけです。 IO "work"ノードの多くは、「ちょっと、OS、読み取り準備ができたデータでネットワーク接続を取得しましたか?」または「OSよ、未処理のファイルシステム呼び出しのいずれかでデータの準備ができていますか?」内部アルゴリズムとイベントループエンジンの設計に基づいて、ノードはJavaScriptの「ティック」を1つ選択して実行し、実行してからプロセスを繰り返します。それがイベントループの意味です。 Nodeは基本的に常に「実行するJavaScriptの次の小さな部分は何か?」を決定してから実行します。これは、OSがIO完了したこと、およびsetTimeoutまたはprocess.nextTickへの呼び出しを介してJavaScriptでキューに入れられたものを考慮します。

2:これらのsetTimeoutがバックグラウンドで実行され、より多くのリクエストが来て実行されている場合、バックグラウンドで非同期実行を実行するのは、EventLoopについて話しているものですか?

JavaScriptはバックグラウンドで実行されません。プログラムのすべてのJavaScriptは、一度に1つずつフロントおよびセンターで実行されます。舞台裏で行われるのは、OSがIOを処理し、ノードがその準備が整うまで待機し、ノードが実行を待機しているjavascriptのキューを管理することです。

3:JS Engineは、EventLoopに配置できるように、それが非同期関数であるかどうかをどのように知ることができますか?

ノードコアには、システムコールを行い、ノードがOSまたはC++を呼び出さなければならないためにこれらがどれであるかを知っているため、非同期の固定機能セットがあります。基本的に、すべてのネットワークとファイルシステムIOおよび子プロセスの対話は非同期であり、JavaScriptがノードに何かを非同期的に実行させる唯一の方法は、ノードコアライブラリが提供する非同期関数の1つを呼び出すことです。独自のAPIを定義するnpmパッケージを使用している場合でも、イベントループを生成するために、最終的にそのnpmパッケージのコードはノードコアの非同期関数の1つを呼び出し、ノードがティックの完了を認識し、イベントを開始できる場合再びループアルゴリズム。

4イベントループはコールバック関数のキューです。非同期関数が実行されると、コールバック関数がキューにプッシュされます。 JavaScriptエンジンは、非同期関数の後のコードが実行されるまで、イベントループの処理を開始しません。

はい、これは事実ですが、誤解を招きます。重要なことは、通常のパターンです:

//Let's say this code is running in tick 1
fs.readFile("/home/barney/colors.txt", function (error, data) {
  //The code inside this callback function will absolutely NOT run in tick 1
  //It will run in some tick >= 2
});
//This code will absolutely also run in tick 1
//HOWEVER, typically there's not much else to do here,
//so at some point soon after queueing up some async IO, this tick
//will have nothing useful to do so it will just end because the IO result
//is necessary before anything useful can be done

そのため、同じティック内でメモリ内のすべてのフィボナッチ数を同期的にカウントするだけで、イベントループを完全にブロックできます。そうすると、プログラムが完全にフリーズします。それは協調的な並行性です。 JavaScriptのティックごとに、妥当な時間内にイベントループを生成する必要があります。そうしないと、アーキテクチャ全体が失敗します。

81
Peter Lyons

Philip Robertsによる素晴らしいビデオチュートリアルがあります。これは、JavaScriptイベントループを最も単純かつ概念的な方法で説明しています。すべてのjavascript開発者は一見する必要があります。

こちらが動画です リンク Youtubeで。

56
sam100rav

ホストプロセスがシングルスレッドであるとは思わないでください。シングルスレッドとは、JavaScriptコードを実行するホストプロセスの部分です。

バックグラウンドワーカー を除き、これらはシナリオを複雑にします...

したがって、すべてのjsコードは同じスレッドで実行され、jsコードの2つの異なる部分を同時に実行する可能性はありません(したがって、管理するための同時実行性はありません)。

実行中のjsコードは、ホストプロセスがイベントループから最後に選択したコードです。コードでは、基本的に2つのことを実行できます。同期命令を実行することと、イベントが発生したときに将来実行される関数をスケジュールすることです。

サンプルコードの私の心的表現は次のとおりです(注意:それは、ブラウザー実装の詳細がわからないだけです!)。

console.clear();                                   //exec sync
console.log("a");                                  //exec sync
setTimeout(                //schedule inAWhile to be executed at now +1 s 
    function inAWhile(){
        console.log("b");
    },1000);    
console.log("c");                                  //exec sync
setTimeout(
    function justNow(){          //schedule justNow to be executed just now
        console.log("d");
},0);       

コードの実行中、ホストプロセスの別のスレッドは、発生しているすべてのシステムイベント(UIのクリック、読み取ったファイル、受信したネットワークパケットなど)を追跡します。

コードが完了すると、イベントループから削除され、ホストプロセスはチェックに戻り、さらに実行するコードがあるかどうかを確認します。イベントループには、さらに2つのイベントハンドラーが含まれています。1つは現在実行されるもの(justNow関数)、もう1つはもう1つ(inAWhile関数)です。

ホストプロセスは、発生したすべてのイベントを照合して、それらに登録されているハンドラがあるかどうかを確認します。 justNowが待機しているイベントが発生したため、コードの実行が開始されました。 justNow関数が終了すると、イベントループをもう一度チェックし、イベントのハンドラーを探します。 1秒が経過すると、inAWhile関数などを実行します。

10
Andrea Parodi