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インデックスの詳細

推薦する

JSX を使用してマークアップ コンポーネント スタイルの開発を作成する例 (フロントエンドのコンポーネント化)

目次JSX環境の構築NPMを初期化するwebpackをインストールするBabelをインストールするw...

vue+springbootでログイン機能を実現

この記事の例では、ログイン機能を実現するためのvue+springbootの具体的なコードを参考まで...

数千万のデータを扱うMySQLのページングクエリのパフォーマンスを最適化する

MySQL のデータ量が多い場合、制限ページングが使用されます。ページ数が増えると、クエリの効率が低...

Linux で複数の mysql5.7.19 (tar.gz) ファイルをインストールする方法

LinuxでのMySQL-5.7.19バージョンの初心者向けの最初のインストールについては、前の記事...

プロジェクトにおける CSS グリッドシステムの柔軟な使用方法の詳細な説明

序文CSS グリッドは通常、さまざまなフレームワークにバンドルされていますが、実際のビジネス ニーズ...

MySQL セキュリティ管理の詳細

目次1. 順番に紹介する2. ユーザーを作成する3. ユーザーアカウントを削除する4. アクセス権5...

MySQLのkillがスレッドをkillできない理由

目次背景問題の説明原因分析シミュレーションする総括する背景日常の使用において、MySQL で個別また...

JavaScript DOMContentLoaded イベントのケーススタディ

DOMContentLoaded イベント文字通り、DOM がロードされた後に実行されます。 win...

Vueカウンターの実装

目次1. カウンターの実装2. 成果を達成する1. カウンターの実装ページにカウンターを実装するだけ...

jQueryカルーセル機能を実装する方法

この記事では、jQueryカルーセル機能の実装コードを参考までに共有します。具体的な内容は次のとおり...

Typora コードブロックのカラーマッチングとタイトルシリアル番号実装コード

効果: タイトルには独自のシリアル番号があり、コードブロックには配色があり、コードブロックの左上隅に...

Vueで複数の添付ファイルをアップロードする実装例

目次序文コアコードコードのファイル表示部分序文この記事では主に、Vue プロジェクトでの添付ファイル...

Nodeイベントループの包括的な理解

目次ノードイベントループイベントループ図メインスレッドイベントループタイマーキューの仕組み投票キュー...

Linux で MySQL 8.0 サービスを完全に削除する方法

この記事を読む前に、Linuxコマンド、特にcentos7.3環境でyumを使用してインストールされ...

Webフロントエンドインターフェースの設計に必須のスキル

[必須] ユーザーインターフェースPhotoShop/花火デザインアーティストと協力して、スケッチを...