🐵
JavaScript/TypeScript の非同期処理をめっちゃわかりやすく整理してみた
JavaScript/TypeScriptを書いていると必ず出会う「非同期処理」。
Promise や async/await を使っているけど、「なんでこうなってるの?」「awaitって実際なにしてるの?」と疑問に思うこと、ありませんか?
この記事では、非同期処理の基本から await の役割までを整理します。
同期処理と非同期処理の違い
同期処理
一個ずつ順番に実行。前の処理が終わるまで次に進まない。
console.log("A");
console.log("B");
console.log("C");
// 出力: A → B → C
非同期処理
「終わったら教えてね」と登録しておき、待たずに次へ進む。
console.log("A");
setTimeout(() => console.log("B"), 1000); // 1秒後に実行
console.log("C");
// 出力: A → C → (1秒後) B
👉 非同期は 待っている間も他の処理を止めないための仕組み。
特に JavaScript はシングルスレッドなので、同期で待ってしまうと UI がフリーズしてしまいます。
Promise とは?
非同期処理の結果を入れる「未来の箱」。
-
pending(まだ終わってない) -
fulfilled(成功して値が入った) -
rejected(失敗してエラーが入った)
の3つの状態を持ちます。
const p = fetch("/api/user");
console.log(p); // Promise { <pending> }
async / await の役割
await なし
const res = fetch("/api/user");
console.log(res);
// → Promise { <pending> } 中身はまだない
await あり
const res = await fetch("/api/user");
console.log(res.ok);
// → 実際の Response を使える
await は Promise が解決するまで「この関数の続きを一時停止」し、中身を取り出してくれる構文です。
※実際にスレッドを止めているわけではなく、イベントループに「完了したら再開して」と登録しているだけ。
そのため UI 全体は止まらずに他の処理が進みます。
イベントループの詳しい解説を下記の記事でしているので詳しく知りたい方はこちらを読んでみてください。
await の正体
じゃあ await は何をしてるのか?
- Promise の完了を「一旦止まって待つ」構文
- 実際にはスレッドをブロックしているわけじゃない
- 「この関数(async 関数)の続きを一時停止 → Promise 解決後に再開」してくれる
async function main() {
console.log("A");
const res = await fetch("/api/user"); // ← Promise が終わるまでこの関数だけ一時停止
console.log("B"); // fetch 完了後に再開
}
main();
console.log("C");
// 実行順: A → C → (fetch 完了後) B
await は「非同期を同期っぽく書けるようにする糖衣構文」なんです。
実際の裏側は全部非同期のまま。
try/catch と非同期エラー処理
非同期処理のエラーも await でシンプルに扱えます。
Promise を .then().catch() で書くとネストが深くなりがちですが、
async/await を使えば同期処理と同じ感覚で try/catch が書けます。
try {
const data = await fetch("/api/user").then(r => r.json());
console.log(data);
} catch (e) {
console.error("API失敗:", e);
}
メリット
- 同期処理と同じパターンでエラーハンドリングができる
- 例外が
throwされればそのままcatchに流れるのでシンプル
まとめ
- 同期処理は「順番どおり」だが、待つ間にアプリが固まる
- 非同期処理は「終わったら教えてね」で UI を止めない
- Promise は「未来の値」を扱う箱
- await は Promise の中身を取り出して「同期っぽく」書けるようにする構文
- 非同期処理のエラーも try/catch で自然に扱える
Discussion