🔄

イベントループ — setTimeout(fn, 0)が即座に実行されない理由

Call Stack、Task Queue、Microtask Queueの実行順序

console.log('1');
setTimeout(() => console.log('2'), 0);
Promise.resolve().then(() => console.log('3'));
console.log('4');
// 出力: 1, 4, 3, 2

0msタイムアウトなのにPromiseの後に出るのはなぜ?イベントループの実行順序のせいだ。

3つのキュー

Call Stack — 現在実行中の関数。同期コードはここで順次実行。

Microtask Queue — Promise.then、MutationObserver、queueMicrotask。Call Stackが空になると全Microtaskを完全に空にするまで実行。Task Queueより優先。

Task Queue(Macro Task) — setTimeout、setInterval、I/Oコールバック、requestAnimationFrame。Microtask完全排出後に1つずつ実行。

V8の実装

V8自体はイベントループを実装しない。ブラウザではlibevent/OSイベントループが、Node.jsではlibuvが担当。V8はCall StackとMicrotask Queueのみ管理。

キーポイント

1

Call Stackの同期コードが全て終わってから非同期コールバックが実行される

2

Microtask(Promise.then)がTask(setTimeout)より常に先に実行

3

Microtask Queueは全て空になるまで実行 — 途中にTaskが挟まらない

4

V8はCall Stack + Microtaskのみ管理、イベントループはブラウザ/libuvが担当

ユースケース

非同期実行順序デバッグ — なぜこのコールバックがあれより先に実行されるか UI応答性 — 重い作業をsetTimeoutで分割してレンダリング機会を確保