JavaScriptイベント実行メカニズムの深い理解

JavaScriptイベント実行メカニズムの深い理解

序文

イベント ループに精通し、ブラウザの動作メカニズムを理解することは、JavaScript の実行プロセスを理解し、動作上の問題をトラブルシューティングするのに非常に役立ちます。以下は、ブラウザ イベント ループのいくつかの原則と例の概要です。

ブラウザJS非同期実行の原理

  • JS はシングルスレッドなので、一度に 1 つのことしか実行できません。では、なぜブラウザは非同期タスクを同時に実行できるのでしょうか?
  • ブラウザはマルチスレッドなので、JS が非同期タスクを実行する必要がある場合、ブラウザは別のスレッドを開始してタスクを実行します。
  • つまり、JS はシングルスレッドであり、JS コードを実行するスレッドは 1 つだけであり、それがブラウザによって提供される JS エンジン スレッド (メイン スレッド) であることを意味します。ブラウザにはタイマー スレッドや HTTP リクエスト スレッドもありますが、これらは主に JS コードの実行には使用されません。
  • たとえば、メイン スレッドで AJAX リクエストを送信する必要がある場合、このタスクは別のブラウザー スレッド (HTTP リクエスト スレッド) に引き渡されて、実際にリクエストが送信されます。リクエストが戻った後、コールバックで実行する必要がある JS コールバックは、実行のために JS エンジン スレッドに引き渡されます。
  • つまり、リクエストを送信するタスクを実際に実行するのはブラウザであり、JS は最終的なコールバック処理の実行のみを担当します。したがって、ここでの非同期性は JS 自体によって実装されているのではなく、実際にはブラウザによって提供される機能です。

ブラウザのイベントループ

実行スタックとタスクキュー

JS はコードを解析するときに、同期コードを特定の場所、つまり実行スタックに順番に並べ、その中の関数を 1 つずつ実行します。
非同期タスクが発生すると、他のスレッドに引き継がれて処理されます。現在の実行スタック内のすべての同期コードが実行された後、完了した非同期タスクのコールバックがキューから取り出され、実行スタックに追加されて実行が続行されます。
非同期タスクに遭遇すると、そのタスクは他のスレッドに引き渡される、などとなります。
他の非同期タスクが完了すると、コールバックはタスク キューに配置されて実行されます。

マクロタスクとマイクロタスク

タスクの種類に応じて、マイクロタスクキューとマクロタスクキューに分けられます。
イベント ループ中、同期コードが実行された後、実行スタックはまずマイクロタスク キューに実行するタスクがあるかどうかを確認します。ない場合は、マクロタスク キューに実行するタスクがあるかどうかを確認します。
マイクロタスクは通常、現在のループで最初に実行されますが、マクロタスクは次のループまで待機します。したがって、マイクロタスクは通常、マクロタスクの前に実行され、マイクロタスク キューは 1 つだけですが、マクロタスク キューは複数存在する場合があります。さらに、クリックやキーボード イベントなどの一般的なイベントもマクロ タスクに属します。

一般的なマクロタスク:

  • タイムアウトを設定する()
  • 間隔を設定する()
  • UIインタラクションイベント
  • 投稿メッセージ
  • setImmediate() -- ノードJs

一般的なマイクロタスク:

  • promise.then()、promise.catch()
  • 新しい MutaionObserver()
  • process.nextTick() -- nodeJs

次の例:

console.log('同期コード1');
タイムアウトを設定する(() => {
    コンソールログ('setTimeout')
}, 0)

新しいPromise((resolve) => {
  console.log('同期コード2')
  解決する()
}).then(() => {
    コンソールログ('promise.then')
})
console.log('同期コード3');

// 最終出力: "同期コード 1"、"同期コード 2"、"同期コード 3"、"promise.then"、"setTimeout"

具体的な分析は以下のとおりです。

  • setTimeout コールバックと promise.then は両方とも非同期で実行され、すべての同期コードの後に​​実行されます。
  • promise.then は後に記述されますが、マイクロタスクであるため、その実行順序は setTimeout よりも優先されます。
  • 新しい Promise は同期的に実行されますが、promise.then のコールバックは非同期的に実行されます。

注: ブラウザで setTimeout 遅延が 0 に設定されている場合、デフォルトで 4 ミリ秒に設定され、NodeJS では 1 ミリ秒になります。
マイクロタスクとマクロタスクの本質的な違い:

  • マクロタスクの特性: 実行してコールバックする必要がある明確な非同期タスクがあり、他の非同期スレッドがそれらをサポートする必要があります。
  • Microtask の特性: 実行される明示的な非同期タスクはなく、コールバックのみであり、他の非同期スレッドのサポートは必要ありません。

非同期/待機実行順序

特徴

  • 非同期宣言された関数は、関数の戻り値を単純にラップして、いずれにしても promise オブジェクトが返されるようにします (非 promise は promise{resolve} に変換されます)。
  • await ステートメントは async 関数でのみ使用できます。
    • 非同期関数の実行時に、await ステートメントに遭遇すると、まず「通常の実行ルール」に従って await 以降のコンテンツが実行されます (関数コンテンツの実行中に await ステートメントに再度遭遇すると、次に await ステートメントのコンテンツが実行されることに注意してください)。
    • 実行後、すぐに async 関数から抜け出し、メインスレッドの他のコンテンツを実行します。メインスレッドの実行後、await に戻り、次のコンテンツの実行を継続します。


定数a = 非同期() => {
  コンソールにログ出力します。
  b() を待機します。
  e() を待機します。
};

定数b = 非同期() => {
  console.log("b 開始");
  c() を待機します。
  console.log("b 終了");
};

定数 c = 非同期() => {
  console.log("c 開始");
  d() を待機します。
  console.log("c 終了");
};

定数d = 非同期() => {
  コンソールにログ出力します。
};

定数e = 非同期() => {
  コンソールにログ出力します。
};

コンソールにログ出力します。
();
コンソールログ('終了');

運用結果

個人分析

  • 現在の同期環境では、まず console.log('start'); を実行して 'start' を出力します。
  • 同期関数 a() に遭遇したら、a() を実行します。
  • a() は sync/await 構造です。関数内で console.log("a") に遭遇すると、'a' が出力されます。await に遭遇すると、非同期関数である await b() が宣言されます。実行は関数 b() で行われます。 (同様の操作ですが、自分で考えました) await b() の後の内容をマイクロタスク キューにプッシュします。 [await b()] と書くこともできます。
  • b() は sync/await 構造です。これを順番に実行すると、console.log("c start") に遭遇し、'c start' を出力します。await 宣言 await c() に遭遇すると、非同期関数となり、関数 c() を実行して実行されます。そして、await c() の後のコンテンツをマイクロタスク キューにプッシュします。これを [await c(), then await b()] と覚えることができます。
  • c() は sync/await 構造です。これを順番に実行すると、console.log("b start") に遭遇し、'b start' が出力されます。非同期関数である await ステートメント await d() に遭遇すると、関数 d() が実行され始めます。そして、await d() の後のコンテンツをマイクロタスク キューにプッシュします。 [await d(), await c(), await b()] と覚えることができます。
  • d() では、順次実行により console.log("") に遭遇し、'd' が出力され、d() 関数が終了します。
  • これはd()を実行した後です。実行する非同期関数はありません。このとき同期環境に入り、a()以降の内容が実行されます。
  • console.log("end") に遭遇すると、'end' が出力されます。このとき、同期環境のメインスレッドが実行され、マイクロタスクキューにマイクロタスクがあるかどうかがチェックされます。
  • マイクロタスクキューには[after await d()、after await c()、after await b()]のマイクロタスクがあり、await d()以降の内容が実行されます。
  • wait d() の後の内容は console.log("c end") となり、'c end' が出力されます。この時点でコンテンツが実行され、その後マイクロタスクキュー[after await c()、after await b()]からチェックされ、await c()後のコンテンツが実行されます。
  • await c(), console.log("b end"); を実行した後の内容は、'b end' と出力されます。この時点でコンテンツが実行され、その後マイクロタスクキューから[after await b()]をチェックしてawait b()後のコンテンツを実行します。
  • await d() の後の内容は await e() です。await 文に遭遇すると、e() が実行されます。そして、判断すると、await e() の後に実行するコードがないので、タスク キューに入る必要はありません。
  • e() を実行し、次に console.log("e"); を順に実行して 'e' を出力します。関数はこの時点で終了します。
  • マイクロタスクキュー[]にマイクロタスクが存在しないため、実行が終了します。同期環境に入ります。

これで、JavaScript のイベント実行メカニズムの詳細な理解に関するこの記事は終了です。JavaScript のイベント実行メカニズムに関するより関連性の高いコンテンツについては、123WORDPRESS.COM の以前の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • いくつかの面接の質問を使ってJavaScriptの実行メカニズムを調べる
  • JavaScript実行メカニズムの詳細な説明
  • JavaScriptの実行メカニズムを徹底的に理解する
  • JavaScript実行メカニズムの詳細な紹介

<<:  MySQLインスタンスを安全にシャットダウンする方法

>>:  MySQLインデックスの詳細

推薦する

ログインフォームを実装するためのReactサンプルコード

Vue ユーザーとして、React を拡張する時が来ました。antd の導入、less と rout...

MySQLデータベース入門:マルチインスタンス構成方法の詳しい説明

目次1. マルチインスタンスとは2. 複数インスタンスのインストールの準備3. MYSQLの複数イン...

CSSはリストのスタイルを設定し、ナビゲーションメニューの実装コードを作成します。

1. リストシンボルを設定するlist-style-type: attribute; //リストの...

JS ES6 非同期ソリューション

目次最初にコールバック関数を使用するes6 非同期処理モデルこの非同期モデルに合わせたAPI: pr...

CSS と HTML とフロントエンド テクノロジーのレイヤー図

フロントエンドテクノロジー層 (写真は少し極端ですが、参考までに) Javascript と DOM...

CSS属性のマージンの理解について話す

1.マージンとは何ですか?マージンは、要素の周囲のスペースの間隔を制御するために使用され、視覚的にス...

MySQL テーブルパーティションの使用法と基本原理の詳細な説明

目次パーティションテーブルとはパーティションテーブルの適用シナリオパーティションテーブルの制限パーテ...

Dockerコンテナにホストディレクトリへの書き込み権限がない場合の解決策

Docker コンテナを適用する場合、多くの場合、ホスト ディレクトリを Docker コンテナにマ...

JS ES の新機能: 拡張演算子の紹介

1. スプレッド演算子スプレッド演算子は 3 つのドット ... で、複数の引数 (関数呼び出しなど...

MySQL 文字セットの文字化けとその解決方法

序文文字セットは、一連のシンボルとエンコード規則です。Oracle データベースでも MySQL デ...

Tomcat マルチポートドメイン名アクセスと gzip 圧縮方式を有効にする構成

1. デフォルトのポート8080に加えて、ドメイン名のアクセスとserver.xmlのオープンにポー...

vue.js 動的コンポーネントの詳細な説明

:動的コンポーネントv-bind:is="component name" を使用...

MySQL バッチ追加および保存メソッドの例

ストレステストにログインする際には、多くの異なるユーザーが必要となり、データベースに新しいデータを追...

image/x-png の ContentType について

これにより、png ファイルのアップロードも不可能になりました (後で情報を調べたところ、レジストリ...

Vue における LocalStorage と SessionStorage の違いと使い方

目次LocalStorageとはSessionStorageとはLocalStorage と Ses...