概要
JavaScriptは「シングルスレッドの同期言語」ではない。
それは**“イベントループとキューによって非同期的に設計された、制御構造としてのランタイム”**である。
非同期処理(setTimeout
、Promise
、fetch
など)は、
ただ「後で実行されるもの」ではない。
それは実行キューの設計、マイクロタスク/マクロタスクの順序、スタックの解放と連動したアーキテクチャ的判断の集合体である。
本稿では、イベントループの動作原理、非同期設計の基礎、マイクロタスクの制御戦略について明快に整理する。
1. JavaScriptのコア構造:シングルスレッド + イベントループ
[ Call Stack ] → [ Task Queue / Microtask Queue ] → [ Event Loop ]
- ✅ JavaScriptは1つのスレッドで実行される
- ✅ 非同期処理は 「イベントループによって後で処理される」
2. コールスタックとタスクキューの基本理解
console.log('A');
setTimeout(() => console.log('B'), 0);
console.log('C');
- 出力順:A → C → B
- ✅
setTimeout(fn, 0)
でも「すぐ実行される」わけではない - ❗ スタックが空になるまで イベントループはタスクを実行しない
3. マイクロタスク(Promise.thenなど)の優先順位
console.log('start');
Promise.resolve().then(() => {
console.log('microtask');
});
setTimeout(() => {
console.log('macrotask');
}, 0);
console.log('end');
- 出力順:start → end → microtask → macrotask
- ✅ マイクロタスクはマクロタスクよりも常に先に実行される
4. 実行順序と安定した設計のための戦略
- ✅
Promise.then
やqueueMicrotask
は「次のTick」で処理を入れる - ✅ UI描画やネットワーク通信はマクロタスク(
setTimeout
,setImmediate
,setInterval
)に属する - ✅ タスクの先後関係を意識して設計する
5. 非同期設計における具体的な設計判断例
function updateUI() {
// DOM操作
}
function fetchData() {
return Promise.resolve('data');
}
fetchData().then(data => {
queueMicrotask(() => {
updateUI(); // UI操作を確実に次のTickに分離
});
});
- ✅ タスクを分離して安全に操作(DOM破壊を回避)
設計判断フロー
① 非同期処理が完了するタイミングは「いつのキュー」か?
② タスクは他の処理と衝突しないよう順序設計されているか?
③ UI描画・状態変更が交錯していないか?(→ 分離設計を行う)
④ PromiseとsetTimeoutを混在させて競合していないか?
⑤ マイクロタスクの使用は適切か?(次Tickでの安全な再実行)
よくあるミスと対策
❌ setTimeout(..., 0)
を「すぐ実行される」と誤認
→ ✅ 実行はスタックが空になった後。即時実行ではない
❌ UI更新とfetch処理を同期的に書き、描画が崩壊
→ ✅ Promiseの中にUI更新を分離、必要なら queueMicrotask
で後回しに
❌ .then()
と setTimeout
の順序が不定になり、競合バグ
→ ✅ それぞれのタスク種別(micro vs macro)を把握し、制御構造を明示する
結語
イベントループとは「順番に処理するキュー」ではない。
それは**“非同期という名の設計構造を、マルチフェーズの実行タイミングとして制御する実行戦略”**である。
-
Promise
やsetTimeout
は「実行されるタイミング」が違う - コールスタック → マイクロタスク → マクロタスク の順序を理解する
- 非同期設計とは「いつ実行されるべきか」を定義することである
JavaScriptにおけるイベントループの理解とは、
“処理の秩序と構造をタイミングで支配する戦略的設計”である。