イベントループ — *JS がシングルスレッドなのに速い理由*
イベントループ — *JS がシングルスレッドなのに速い理由*
🎯 このレッスンを読み終えたら
このレッスンを読み終えると、以下の3つを自信を持って扱えるようになります。
- ▸✅ イベントキャプチャリング・バブリング・委譲 (delegation) パターン
- ▸✅
addEventListenerのクリーンアップ漏れによるメモリリーク - ▸✅ debounce / throttle を適用するタイミング
学習目標をチェックリストとして手元に置き、すべてに答えられるようになったらレッスンを閉じてください。
シングルスレッド + イベントループ
核心を一言で
JS は一度に一つだけ処理するシングルスレッド言語ですが、膨大な数のタスクを並行して処理できます。その秘密はイベントループ (Event Loop) と非同期処理の外部委譲です。
シングルスレッド — 落とし穴?
slowOperation が5秒かかると、その間は何もできません。ユーザーのクリックも無視されます。これがブロッキングです。
解決策: ブロッキング処理を外部に委ねて、JS は次の処理を続けます。
イベントループの動作
ブラウザ・Node.js が別スレッドで非同期処理 (タイマー・ネットワーク・ファイル) を実行します。完了するとQueue にコールバックを追加し、Stack が空になった時点で Event Loop が取り出して実行します。
最も有名なクイズ
setTimeout(..., 0) なのに、なぜ 2 が最後に出力されるのか?
- ▸
setTimeoutのコールバックは Task Queue に入る - ▸現在のコード (
console.log("3")) が終わった後に処理される
setTimeout の遅延時間 0 は即時ではなく、現在のコードが終わり次第できるだけ早くという意味です。
Microtask と Macrotask
Queue には2種類あります:
- ▸Microtask — Promise・queueMicrotask。優先度高
- ▸Macrotask — setTimeout・イベント・I/O。優先度低
現在のコードが終わると:
1. Microtask をすべて処理 (3)
2. 次に Macrotask を一つ処理 (2)
非同期 = 外部に委ねること
これらは JS ではなく環境 (ブラウザ・Node) が処理し、完了時にコールバックを Queue に追加します。JS 本体は待たずに次のコードへ進みます。
なぜ速いのか
1万人が同時アクセスしてもユーザーごとに別スレッドを作らず:
- ▸リクエスト 1 開始 → DB 照会を外部委譲 → 次のリクエストを処理
- ▸リクエスト 2 開始 → DB 照会を委譲 → さらに次のリクエストを処理
- ▸DB のレスポンスが返ったら → 各コールバックを処理
1スレッドで大量の並行処理。Node.js・Nginx が少ないメモリで大量トラフィックを捌ける秘訣です。
落とし穴 — CPU バウンドな処理
JS の非同期の強みはI/O 処理にのみ有効です。CPU バウンドな処理は依然としてブロッキングです。
解決策:
- ▸小さなチャンクに分けて setTimeout で処理
- ▸Web Worker (ブラウザ) / Worker Threads (Node) — 別スレッド
まとめ
- ▸JS = シングルスレッド + イベントループ
- ▸非同期処理は外部に委譲
- ▸Microtask (Promise) > Macrotask (setTimeout)
- ▸I/O に強く、CPU に弱い
- ▸CPU 処理は Worker に任せる
⚡ やってみよう — Microtask vs Macrotask の順序
🤖 AI にこう依頼してみよう
このレッスンの概念を理解すれば、AI に具体的な指示が出せるようになります。漠然とした「直して」ではなく、語彙を持ったリクエスト — それがトークン節約の出発点です。
- ▸「このクリックイベントに debounce 300ms を適用して」
- ▸「このイベントハンドラーにクリーンアップ (return () => removeEventListener) も追加して」
なぜこれでトークンが節約できるのか
概念を知らないと、AI の回答を受け取っても「それって何ですか?」と再度聞くことになります。その「再質問」がトークンを消費します。概念を一度理解しておくと、会話が一回で完結します。