2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

JavaScriptのEvent Loopを完全に理解する:タスク、マイクロタスク、非同期処理の構造設計

Posted at

概要

非同期処理のバグや意図しない順序の実行は、仕様を知らずに「動いているからOK」としてしまったコードから生まれる。
その根底にあるのが、Event Loop という実行モデルだ。

Event Loopを理解することは、JavaScriptがいつ、何を、どの順番で実行するのかを制御することであり、
Promise・setTimeout・DOMイベント・fetch など、すべての非同期動作の共通語となる。

本記事では、Event Loopの全体構造を視覚的に・構造的に整理し、現場で即使える判断軸を提供する。


対象環境

ブラウザ・Node.js問わずJavaScript全般(ES6以降)

Event Loopの全体像(概念図)

+-----------------------------+
|        Call Stack          |
+-----------------------------+
|        Task Queue          |
| (setTimeout, I/O, etc)     |
+-----------------------------+
|     Microtask Queue        |
| (Promise.then, queueMicrotask) |
+-----------------------------+

JavaScriptはシングルスレッドのイベント駆動モデル

Event Loopは以下の順序で実行を制御する:

  1. コールスタックが空になるまで同期処理を実行
  2. マイクロタスクキューからすべてのタスクを順番に実行
  3. タスクキュー(マクロタスク)から1つ取り出して実行
  4. 再びマイクロタスク→マクロタスクの順に繰り返す

各構成の意味と用途

✅ コールスタック(Call Stack)

  • 同期処理が積み上がっていく場所
  • 関数の呼び出しやスコープがここに保持される

✅ マイクロタスクキュー(Microtask Queue)

  • Promise.then()
  • MutationObserver
  • queueMicrotask()

→ イベントループ1サイクル内で必ず全て消化される


✅ タスクキュー(Macrotask Queue)

  • setTimeout()
  • setInterval()
  • setImmediate()(Node.js)
  • requestAnimationFrame()

次のイベントループのタイミングで処理される
→ マイクロタスクより後に実行される


実行順の基本原則

console.log('script start');

setTimeout(() => {
  console.log('setTimeout');
}, 0);

Promise.resolve().then(() => {
  console.log('promise');
});

console.log('script end');

✅ 出力順:

script start
script end
promise
setTimeout

Promise.thenマイクロタスクsetTimeoutマクロタスク


高度な順序制御:ネストの実例

Promise.resolve()
  .then(() => {
    console.log('micro 1');
    setTimeout(() => console.log('macro inner'), 0);
  })
  .then(() => console.log('micro 2'));

→ 出力順:

micro 1
micro 2
macro inner

queueMicrotask vs setTimeout

queueMicrotask(() => console.log('micro'));
setTimeout(() => console.log('macro'), 0);

queueMicrotask() の方が先に実行される


Node.jsにおける補足:process.nextTick

  • process.nextTick()マイクロタスクよりもさらに優先される
  • 極端に多用すると無限ループのような挙動になることも

設計上の判断軸:いつ何を使うべきか?

処理内容 適切なAPI 理由
非同期だけど優先順位が高い Promise.then() マイクロタスクで即実行される
フレームのタイミングに合わせたい requestAnimationFrame() 描画に同期させたい
非同期処理を次回に遅延したい setTimeout(fn, 0) 次のイベントループまで待つ
純粋に軽い順序制御だけしたい queueMicrotask() 微細な順序制御に最適

よくある誤解とバグ

❌ setTimeout(fn, 0) は「すぐ」実行される?

→ 実際には最低でも1イベントループ遅延する


❌ Promise.then の中で同期的に例外が飛ばない

try {
  Promise.resolve().then(() => {
    throw new Error('boom');
  });
} catch (e) {
  // ❌ ここでは捕捉されない
}

→ ✅ .catch() をチェーンすべき


結語

Event Loopとは、JavaScriptの非同期処理を「見える化」した構造であり、
その設計を知ることは、いつ・なにが・なぜ実行されるのかを意図的に制御できるということである。

  • PromiseとsetTimeoutが逆転する理由
  • 非同期処理の“順序”がなぜ狂うのか
  • フレームレートに合わせるにはどうすればいいのか

これらすべての問いは、Event Loopの理解によって明確に答えられる。

JavaScriptを「動かす」から「支配する」へ。
そのために最初に習得すべきは、言語の内部構造=Event Loopである。

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?