#
ドキュメント

Document

自分のための備忘録です。

イベントループ

イベントループの概要

JavaScript の イベントループ( event loop) は以下のように動作します。

  1. コールスタックの処理
    • コードが順次実行され、関数呼び出しなどがコールスタックに積まれます。
    • コールスタックが空になるまで、他のタスク(マイクロタスクやタイマー)は実行されません。
  2. マイクロタスクの処理
    • コールスタックが空になった時点で、マイクロタスクキューに溜まったタスク(例:Promise.then)を順次実行します。
  3. タスクキューの処理
    • マイクロタスクキューが空になった後に、通常のタスクキュー(例:setTimeout のコールバック)が処理されます。

非同期処理の種類

JavaScript の非同期処理は、イベントループを基盤に以下の2種類に分類されます:

分類 代表的な例 実行順序の優先度
マイクロタスク Promise.then, MutationObserver コールスタックが空の直後に実行
マクロタスク setTimeout, setInterval, I/O マイクロタスクがすべて完了した後に実行

イベントループの仕組み

  1. コールスタック(同期処理)が空になるまで実行。
  2. マイクロタスクキューのタスクを順次実行。
  3. タスクキュー(マクロタスク)を順次実行。
  4. 1~3を繰り返す。

マイクロタスク

マイクロタスクは「現在のコールスタックの処理が完了した直後」に実行されます。
優先度が高く、以下が該当します:

  • Promise.then, catch, finally: Promise の非同期コールバック。
  • MutationObserver: DOM の変更を監視するコールバック。

マクロタスク

マクロタスクは「マイクロタスクがすべて処理された後」に実行されます。
以下が該当します:

  • setTimeout / setInterval: タイマーのコールバック。
  • I/O 処理: ファイルの読み書きやネットワーク通信。

setTimeout は非同期処理といえるか?

setTimeout のコールバックは非同期処理といえますが、マクロタスクに分類されます。 指定された遅延時間が経過しても、マイクロタスクが優先されるため、コールスタックやマイクロタスクが処理されるまで実行されません。

実行順序の例

console.log('Start');

setTimeout(() => {
  console.log('setTimeout'); // マクロタスク
}, 0);

Promise.resolve().then(() => {
  console.log('Promise'); // マイクロタスク
});

const observer = new MutationObserver(() => {
  console.log('MutationObserver'); // マイクロタスク
});

const target = document.createElement('div');

observer.observe(target, { attributes: true });

target.setAttribute('data-test', 'value');

console.log('End');

// Start
// End
// Promise
// MutationObserver
// setTimeout

注意点

重い同期処理(例: 長いループ)は、非同期処理を遅延させます。

コールスタックがすべて処理されるまで、マイクロタスクもマクロタスクも実行されません。 最小遅延時間:setTimeout(fn, 0) の実行は「即時実行」ではなく、タイマーの最小遅延時間(通常4ms)に依存します。

マイクロタスクの過剰な使用:大量のマイクロタスクが登録されると、マクロタスクが遅延し、UI 更新や I/O 処理が滞る可能性があります。

おすすめの設計方法

  • 重い処理の分割:非同期処理(setTimeout や requestAnimationFrame)で小分けに実行。
  • UI のフリーズ回避:必要に応じて Web Worker を活用して別スレッドで処理。
  • マイクロタスクの適切な使用:Promise を多用する場合、処理が意図した順序で実行されるよう設計。