JavaScript 実行モデルの解説:同期・非同期処理の構造と制御フロー
JavaScript はシングルスレッドで動作する言語でありながら、非同期処理を柔軟かつ効率的に扱うことができます。その根底には、JavaScript の実行モデルが存在します。本記事では、スクリプトの開始から非同期タスクの実行に至るまでの処理構造を、6 つのステップに分けて図解付きで詳しく解説します。
1. 同期コードフェーズ(Call Stack が空になるまで)
JavaScript の処理は、まずグローバルスコープにおける同期コードの実行から始まります。関数の呼び出しなどは、Call Stack 上で順に積まれ、完了後に pop されます。
2. 外部 API 呼び出しによる非同期処理の登録
同期処理の中で、非同期 API(例: setTimeout
, fetch
)が呼び出されると、実行自体はホスト環境に渡され、結果は後から戻ってきます。
3. Promise の生成とマイクロタスク登録
Promise
は非同期処理の表現手段として使われ、状態変化(resolve/reject)時にマイクロタスクが登録されます。
4. Call Stack の空化とイベントループの開始
同期処理が全て終了し、Call Stack が空になると、イベントループが非同期タスクの実行を開始します。
5. イベントループによるマイクロ・マクロタスクの順次処理
Call Stack が空になった後、イベントループが 1 ループ (tick) ごとにマイクロタスク → マクロタスクの順に実行していきます。
マイクロタスクが存在するとき、存在するすべてのマイクロタスクが実行されます。マクロタスクが存在するとき、先頭の一つだけ実行され、次の tick に進みます。
6. 外部 API 等によるタスクの登録
外部 API 等による非同期処理の完了時、対応するコールバックが JavaScript エンジン側に通知されます。
-
setTimeout
やsetInterval
のような API は、指定された遅延時間が経過すると、対応するコールバック関数がマクロタスクキューに登録され、イベントループによって後続の tick で順次実行されます。 -
一方で、
fetch
のような Promise を返す非同期 API は、ネットワーク応答の完了後にホスト環境からresolve()
が 直接 JavaScript エンジンに呼び出され、.then()
などのコールバックがマイクロタスクキューに登録されます。マクロタスクキューは介しません。
これらの処理(タイマーやネットワーク処理)はすべて ホスト環境側の非同期スレッドやシステムによって管理されており、JavaScript のイベントループとは 並行かつ競合のない形で安全に連携しています。
まとめ
このように、JavaScript の実行モデルは明確に「同期処理 → 非同期タスク登録 → イベントループによる処理」の三段構成で整理されています。これを理解することで、複雑な非同期コードの制御やデバッグが格段にしやすくなります。
2025 © うの.