JavaScriptは、ウェブブラウザで動作する動的なプログラミング言語です。特に、非同期処理を効率的に扱うために「イベントループ(Event Loop)」という仕組みが重要な役割を果たしています。この記事では、JavaScriptのイベントループがどのように機能し、ブラウザ内でのコード実行がどのように管理されているのかを完全に理解するために、詳細に解説します。
イベントループとは?
JavaScriptのイベントループは、コードの実行を管理するための仕組みで、特に非同期処理を扱う際にその重要性が増します。簡単に言うと、イベントループは「実行スタック」と「キュー」の間でコードの実行を効率よく切り替える役割を持っています。

JavaScriptはシングルスレッドで動作するため、一度に一つのタスクしか実行できません。ですが、非同期処理(例えば、タイマーやネットワークリクエストなど)は、すぐに結果を返さない可能性があり、そのためJavaScriptはその処理をバックグラウンドで待機させ、他のコードの実行を優先させます。この「待機」のプロセスを管理するのがイベントループです。
実行スタック(Call Stack)
実行スタックは、現在実行中の関数や処理を保持するスタック領域です。JavaScriptのコードが実行されると、関数がスタックにプッシュされ、その関数が終了するとポップされます。例えば、次のようなコードを見てみましょう。
javascriptfunction greet() {
console.log("Hello, World!");
}
greet();
このコードが実行されると、greet
関数が実行スタックに積まれ、関数の中身が実行されます。関数が終了すると、スタックから取り除かれ、次の処理に移行します。
キュー(Queue)
キューは、非同期処理やイベントリスナーからのコールバック関数が待機する場所です。JavaScriptが非同期処理を扱う際、例えばsetTimeout
やfetch
などの操作を行うと、その処理が終了するまでコードの実行が一時的に停止します。その後、非同期処理が終了すると、そのコールバック関数がキューに追加され、スタックが空になると順番に実行されます。
例えば、次のコードでは非同期関数がキューに追加されます。
javascriptconsole.log("Start");
setTimeout(() => {
console.log("This is a delayed message");
}, 2000);
console.log("End");
このコードを実行すると、次のように出力されます:
pgsqlStart
End
This is a delayed message
ここで、setTimeout
のコールバック関数は、2000ミリ秒後に実行されるため、その間に「Start」と「End」が先に出力され、非同期処理が終わった後に「This is a delayed message」が表示されます。
イベントループの流れ
イベントループの基本的な流れは以下の通りです:
- 実行スタックが空でない場合、現在の関数が実行されます。
- 実行スタックが空になると、キューにあるコールバック関数がスタックにプッシュされ、順番に実行されます。
- 非同期操作(例えば、
setTimeout
やfetch
)が完了すると、そのコールバック関数がキューに追加されます。 - イベントループは、このサイクルを繰り返し、必要に応じて新しいタスクを処理します。
マクロタスクとミクロタスク
イベントループにおいて、タスクには2つの種類があります:マクロタスクとミクロタスクです。
マクロタスク
マクロタスクは、主にsetTimeout
やsetInterval
、I/Oイベント(ネットワークやユーザー入力など)などが含まれます。これらのタスクはキューに追加され、スタックが空くと実行されます。
ミクロタスク
ミクロタスクは、Promise
のコールバックやMutationObserver
など、より高い優先度を持つタスクです。ミクロタスクは、現在実行中のコードが終わった後、次に実行される前に優先的に実行されます。つまり、イベントループはマクロタスクを処理する前に、すべてのミクロタスクを処理します。
次のコードを見てみましょう:
javascriptconsole.log("Start");
setTimeout(() => {
console.log("This is a macro task");
}, 0);
Promise.resolve().then(() => {
console.log("This is a micro task");
});
console.log("End");
出力は次のようになります:
pgsqlStart
End
This is a micro task
This is a macro task
ここで、Promise
のコールバック(ミクロタスク)は、setTimeout
のコールバック(マクロタスク)よりも先に実行されることが分かります。
イベントループと非同期処理
イベントループの最も大きな特徴は、非同期処理を効率的に扱う点です。例えば、ネットワークリクエストを行う際、JavaScriptは他のコードをブロックすることなく、その応答を待つことができます。これにより、ユーザーインターフェースがスムーズに動作し続け、シームレスな体験が提供されます。
javascriptconsole.log("Start");
fetch('https://jsonplaceholder.typicode.com/posts')
.then(response => response.json())
.then(data => console.log(data));
console.log("End");
このコードでは、fetch
が非同期で実行されますが、console.log("End")
が先に実行されます。fetch
の結果が返ってきた後に、そのコールバック関数がキューに追加され、順番に実行されます。
まとめ
JavaScriptのイベントループは、コードの実行順序を管理し、非同期処理を効率的に扱うための重要な仕組みです。シングルスレッドで動作するJavaScriptは、実行スタックとキューを駆使して、非同期タスクを適切に処理し、ユーザーインターフェースをブロックせずにスムーズな動作を実現します。
イベントループの理解は、非同期プログラミングを効果的に活用するための鍵となります。この仕組みを理解することで、より高度なJavaScriptのプログラミングを行うことができるようになります。