同期処理とは
同期処理とは、コードを上から順に実行し、1 つの処理が終わるまで次の行に進まない実行モデルです。
console.log("A");
console.log("B");
console.log("C");
これは A → B → C の順に出力されます。直感的でわかりやすいですが、「時間のかかる処理」が来たとき問題が起きます。
問題:同期でブロックすると UI が固まる
JavaScript はシングルスレッドで動きます。つまり、1 つの処理が走っている間は他の何も実行できません。
ネットワーク越しにデータを取得する処理を同期的に書いたと想像してください。その間、ブラウザはスクロールにも、クリックにも反応できなくなります。「ページが固まった」という体験がまさにこれです。
1 本の道に車が 1 台しか走れないとすれば、大型トラックが渋滞を引き起こすのと同じ構造です。
非同期処理とは
非同期処理は「重い処理を外に出して、終わったら知らせてもらう」仕組みです。
ネットワークリクエストやタイマーといった処理はブラウザ(あるいは Node.js のランタイム)が肩代わりして実行します。JavaScript 本体はその間も別のコードを走らせ続けます。処理が終わったら「コールバック」や「Promise の resolve」という形で通知が届きます。
コールスタックとイベントループ
JS エンジンが非同期を実現する核心的な仕組みが、コールスタックとイベントループです。
- コールスタック — 現在実行中の関数が積まれるスタックです。関数を呼ぶと積まれ、終わると取り除かれます。
- タスクキュー — 非同期処理が完了したときに「次に実行すべきコールバック」が並ぶ待ち行列です。
- イベントループ — コールスタックが空になったとき、タスクキューの先頭を取り出して実行する監視係です。
重要なのは「コールスタックが空でないとタスクキューは処理されない」という点です。同期コードがすべて終わってから、非同期のコールバックが順番に処理されます。
setTimeout で体感する
非同期の実行順序を setTimeout で確認してみましょう。
console.log("1: 開始");
setTimeout(() => {
console.log("2: タイムアウト");
}, 0);
console.log("3: 終了");
出力は次の順になります。
1: 開始
3: 終了
2: タイムアウト
setTimeout(..., 0) は「今すぐ」ではなく「現在の同期コードがすべて終わった後」という意味です。コールバックはタスクキューに入り、イベントループが空きを見て初めて実行されます。
この直感に反する順序こそが、非同期の本質をよく示しています。
まとめ
- 同期処理は上から順に実行し、終わるまで次に進みません。シングルスレッドの JS では重い処理がすべてをブロックします。
- 非同期処理は「重い処理をランタイムに委譲し、終わったら通知を受け取る」設計パターンです。
- 実現の仕組みはコールスタック・タスクキュー・イベントループの 3 要素です。コールスタックが空になった瞬間にキューの先頭が処理されます。
setTimeout(..., 0)でさえ同期コードより後に実行されます。これを理解すると、Promise や async/await の挙動も自然と読めるようになります。