ホームページ  >  記事  >  ウェブフロントエンド  >  jsイベントループの例を詳しく解説

jsイベントループの例を詳しく解説

零下一度
零下一度オリジナル
2017-06-26 09:17:381217ブラウズ

以前イベント周回ブログをいくつか読んだことがありましたが、しばらく読んでいなかったのですっかり忘れていたことに気づき、まとめとしてブログを書くことにしました!

まず最初に、イベント ループとは何かを説明しましょう:

私たちの知る限り、ブラウザの js はシングルスレッドです。つまり、同時に実行されるコード セグメントは最大でも 1 つだけです。しかし、ブラウザは非同期リクエストを非常にうまく処理できるのに、なぜでしょうか?まずは画像を見てみましょう
jsイベントループの例を詳しく解説
上の画像から、js メインスレッドに実行スタックがあり、すべての js コードが実行スタック内で実行されることがわかります。コードの実行中に、非同期コード (setTimeout、ajax、promise.then、ユーザーのクリックなど) が発生すると、ブラウザーはこれらのコードをスレッド (ここではビハインドザと呼びます) に入れます。 -scenes thread) 待機しても、メイン スレッドの実行はブロックされません。バックグラウンド スレッドのコードの準備が整ったとき (たとえば、setTimeout 時間が経過し、ajax リクエストが完了したとき)、メイン スレッドはスタック内の残りのコードを実行し続けます。が応答されると、スレッドはそれを処理します。コールバック関数は実行を待機するタスクキューに配置されます。メインスレッドは、スタック内のすべてのコードの実行を終了すると、タスクキューをチェックして、実行すべきタスクがあるかどうかを確認し、実行すべきタスクがある場合、そのタスクは実行スタックに置かれます。実行。現在のタスクキューが空の場合、タスクが到着するまでループで待ち続けます。したがって、これはイベント ループと呼ばれます。

それでは、ここで質問です。タスクキューに多数のタスクがある場合、どのタスクを最初に実行する必要がありますか?

実際には(上の図に示すように)、jsには2つのタスクキューがあり、1つはマクロタスクキュー(タスクキュー)と呼ばれ、もう1つはマイクロタスクキューと呼ばれます

    前者は主にいくつかの相対的な処理を実行するために使用されます一般的な大規模な作業には、setTimeout、setInterval、ユーザー インタラクション、UI レンダリングなどがあります
  • 後者は主に、比較的小さな作業を実行するために使用されます。一般的なものには、Promise、process.nextTick (nodejs) が含まれます
  • では、この 2 つの具体的な違いは何でしょうか? つまり、2 つのタスクが同時に表示された場合、どちらを選択する必要があるのでしょうか?
実際、イベント ループは次のようになります。


    マクロタスクキューが空かどうかを確認します。空でない場合は、次のステップに進みます。 3
  1. マクロタスクキューから一番上のタスクを取得します。キュー) を実行し、実行スタック (1 つだけ) で実行します。 実行後、次のステップに進みます
  2. キューが空かどうかを確認し、空でない場合は次のステップに進み、そうでない場合は 1 (新しいイベント ループを開始します)
  3. マイクロタスク キューからキューの先頭にあるタスク (キュー内で最も長い時間のもの) を取り出し、イベント キューに入れます。 実行後、3 にジャンプします。
  4. 。その中で、コードの実行中に追加されたマイクロタスク タスクは現在のイベント ループ サイクル内で実行されますが、新しく追加されたマクロタスク タスクは、次のイベント ループが実行されるまで待機することしかできません。 1 つのマクロタスクを実行します)
  5. まず、コードを見てみましょう
console.log(1)
setTimeout(function() {
  //settimeout1
  console.log(2)
}, 0);
const intervalId = setInterval(function() {
  //setinterval1
  console.log(3)
}, 0)
setTimeout(function() {
  //settimeout2
  console.log(10)
  new Promise(function(resolve) {
    //promise1
    console.log(11)
    resolve()
  })
  .then(function() {
    console.log(12)
  })
  .then(function() {
    console.log(13)
    clearInterval(intervalId)
  })
}, 0);

//promise2
Promise.resolve()
  .then(function() {
    console.log(7)
  })
  .then(function() {
    console.log(8)
  })
console.log(9)

結果はどうなると思いますか?
ノード環境と Chrome コンソールで出力した結果は次のとおりです:

1
9
7
8
2
3
10
11
12
13

上記の例では、

の最初のイベントループ:


console.log(1)が実行され、出力1
  1. settimeout1が実行され、マクロタスクキューに参加します
  2. setinterval1が実行され、マクロタスクキューに参加します
  3. settimeout2 を実行し、マクロタスクキューに参加します
  4. promise2 が実行され、その 2 つの関数がマイクロタスクキューに追加されます
  5. console.log(9) が実行され、9 が出力されます
  6. の定義によるとイベントループでは、次に新規が実行されます。 追加されたマイクロタスクタスクはキューに入った順に実行されます。 console.log(7) と console.log(8) が出力されます。 マイクロタスクキューは空です。最初のステップに戻り、次のイベント ループに入ります。 この時点でのマクロタスク キューは次のとおりです: settimeout1、setinterval1、settimeout2

  7. 2 番目のイベント ループ:

マクロタスク キューからチーム (settimeout1) を取得して実行すると、出力 2 マイクロタスク キューは空です。戻り、最初のステップに戻り、次のイベント ループに入ります。この時点で、マクロタスク キューは setinterval1、settimeout2

  1. です。

    3 番目のイベント ループ:

マクロタスク キューからチームの先頭のタスク (setinterval1) を取得し、実行して 3 を出力し、新しく生成された setinterval1 をマクロタスク キューに追加します。マイクロタスク キューは空です。最初のステップに戻り、次のイベント ループに入ります。このとき、マクロタスク キューは次のとおりです: settimeout2, setinterval1

  1. 4 回目のイベント ループ:


マクロタスク キューからチーム (settimeout2) を取り出して実行し、出力 10 で新しい Promise の関数を実行します (新しい Promise の関数は非同期操作ではなく同期操作です)。出力 11 でその 2 つの then 関数をマイクロタスクに追加します。行列

  • マイクロタスクキューから、キューの先頭にあるタスクを取り出し、空になるまで実行します。したがって、新しく追加された 2 つのマイクロタスク タスクが順番に実行され、出力 12 と 13 が実行され、setinterval1 はクリアされます。このとき、ブラウザはキューが空かどうかを常に確認して待機します。新しいタスクをキューに追加します。

  • ここで、最初のループで、なぜマクロタスクが最初に実行されないのかと考えているかもしれません。なぜなら、プロセスによれば、最初にマクロタスクキューが空かどうかを確認してから、マイクロタスクキューを確認する必要があるのではないでしょうか?

    理由: jsメインスレッドで最初に実行されているタスクはマクロタスクタスクであり、イベントループの処理に応じて、イベントループはメインスレッドのコードを実行した後にマクロタスクタスクを1つだけ実行するためです。 、マイクロタスクキューに移動し、チームのリーダーにタスクを実行してもらいます。

    注:

    マイクロタスクタスクを実行するとき、マイクロタスクキューが空の場合にのみ次のイベントループに入ります。そのため、新しいマイクロタスクタスクを継続的に生成すると、メインスレッドがマイクロタスクを実行しています。タスクが存在し、マクロタスク タスクを実行する方法がないため、UI レンダリング/IO 操作/Ajax リクエストを実行できないため、この状況を回避する必要があります。 nodejs の process.nexttick では、メインスレッドのブロックを防ぐために最大呼び出し数を設定できます。

    これで、新しい問題、タイマー問題が導入されます。タイマーは本物で信頼できるものですか?たとえば、コマンド setTimeout(task, 100) を実行した場合、そのコマンドは 100 ミリ秒後に正確に実行されますか?実際、上記の議論に基づいて、これは不可能であることがわかります。

    setTimeout(task,100) を実行した後、実際にはタスクが 100 ミリ秒後にマクロタスク キューに入ることが保証されるだけで、すぐに実行できるという意味ではないため、誰もがその理由を知っているはずです。 master スレッドが時間のかかる操作を実行しているか、現在マイクロタスクのキューに多くのタスクがある可能性があるため、これが setTimeout を批判している理由かもしれません。イベントループといくつかの参考文献 その他の素晴らしい記事

    以上がjsイベントループの例を詳しく解説の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

    声明:
    この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。