JavaScriptの実行原理について(1)
今日は、JavaScriptが単一スレッドの言語として、どのようにイベントループとタスクキューの仕組みを通じて、効率的に非同期タスクを処理し、ユーザー体験の滑らかさを確保しているかについて説明します。
この記事では、イベントループ、タスクキュー、そして実際の開発での応用シーンについて詳しく探っていきます。
イベントループの概要
イベントループでは、メインスレッドが現在の同期タスクを実施し終えた後、イベントキューに処理すべきイベントがあるがどうかを確認します。もしイベントがあれば、メインスレッドはそのイベントを取り出し、対応するコールバック関数を実施します。この繰り返すのプロセスがイベントループと呼ばれ、メインスレッドとタスクキューの二つの部分で構成されています。
メインスレッドは同期タスクの実施を担当し、非同期タスクはタスクキューを通じて処理されます。この仕組みにより、非同期タスクが適切なタイミングで挿入されて実行され、JavaScriptの非ブロッキング非同期実行が実現されます。
イベントループの流れは以下の通りです
- メインスレッドがJavaScriptコードを読み込む、それに対応するヒップと実行スタックを形成します
- メインスレッドが非同期タスクを遭遇した場合、それを対応する非同期プロセス(例えば:Web API)に委譲して処理します
- 非同期タスクが完了すると、対応するコールバック関数がタスクキューにプッシュされます
- メインスレッドが同期タスクを実施し終えた後、タスクキューを確認し、タスクがあれば、先入先出の原則に従ってタスクをメインスレッドにプッシュして実施します
- 上記の手順を繰り返す、イベントループが形成されます
同期タスク
同期タスクは、コードが記述された順序に従って一歩一歩実行されるタスクのことです。メインタスクが同期タスクを実行している間、現在のタスクが完了するまで後続のコードの実行がブロックされます。
典型的な同期タスクには、関数の呼び出し、変数の代入、算術演算などがあります。
非同期タスク
非同期タスクとは、メインスレッドが実行されている間に、コールバック関数やその他のメカニズムを通じて他のスレッドやイベントに委任されるタスクのことです。
非同期タスクを実行する際、メインスレッドはタスクの完了を待たずに、後続のコードを続けて実行します。それに以下が含まれます:
- コールバック関数
- Promise
- ジェネレーター
- タイマー
- requestAnimationFrame
- MutationObserver
- process.nextTick
タスクキュー
タスクキューは、マクロタスクキュー(macrotask queue)とマイクロタスクキュー(microtask queue)の2種類に分かれます。JavaScriptエンジンはイベントループの仕組みに従い、現在のマクロタスクが終了した後、マイクロタスクキューを確認し、その中のマイクロタスクを実行してから次のマクロタスクを実行します。このプロセスが絶え間なく繰り返され、イベントループが形成されます。
1. マクロタスク(Macrotasks)
マクロタスクは、比較的大きな粒度のタスクを指し、以下が含まれます:
- すべての同期タスク
- I/O操作(例:ファイルの読み書き、データベースのデータ読み書きなど)
- setTimeout、setInterval
- setImmediate(Node.js環境)
- requestAnimationFrame
- イベントリスナーのコールバック関数 など
2. マイクロタスク(Microtasks)
マイクロタスクは、より小さな粒度で優先度の高いタスクを指し、以下が含まれます:
- Promise の then、catch、finally
- async/await 内のコード
- Generator 関数
- MutationObserver
- process.nextTick(Node.js環境)
タスクの実行プロセス
まず、JavaScriptにおいて、すべてのタスクはメインスレッドで実行されることを明確にする必要があります。タスクの実行プロセスは、同期タスクと非同期タスクの2つのフェーズに分かれます。非同期タスクの処理は、Event Table(イベントテーブル)とEvent Queue(イベントキュー)という2つの主要なフェーズを経て行われます。
Event Tableは、マクロタスクに関連する情報を保存しており、これにはイベントリスナーや対応するコールバック関数が含まれます。特定のタイプのイベントが発生すると、対応するコールバック関数がイベントキューに追加され、実行待ちとなります。
マイクロタスクは、Event Queueと密接に関連しています。実行スタック内のコードが完了した後、JavaScriptエンジンはイベントキューを絶えずチェックします。キューが空でない場合、キュー内のイベントを1つずつ取り出し、対応するコールバック関数を実行します。
タスクキューの実行フローは次のように要約できます:
- 同期タスクはメインスレッドで順番に実行され、非同期タスクはイベントキューで順番待ちし、メインスレッドに実行されるのを待ちます。
- マクロタスクに遭遇した場合、マクロタスクキューに進められ、マイクロタスクに遭遇した場合、マイクロタスクキューに進められます。
- マクロタスクを実行し、完了した後、現在のレイヤーのマイクロタスクをチェックして実行します。
- 次のマクロタスクを実行し、対応するレイヤーのマイクロタスクを実行し、すべてが完了するまでこれを繰り返します。
最後
ここまでお読みいただきありがとうございます。もし何か疑問や質問がありましたら、ぜひご指摘ください。よろしくお願いします。
Discussion