JavaScriptで非同期処理を書く際、Promise・async/awaitの2つの書き方があります。両者は同じ仕組みの上に成り立つ関係ですが、書き心地・エラー処理・並列実行の見え方が異なります。本記事では、両者の違いと使い分けを解説します。
Promiseとは
「未来に結果が決まる値」を表すオブジェクトです。状態は pending → fulfilled or rejected の3つ。.then() .catch()でつなげて処理を書きます。
fetch('/api/user')
.then((res) => res.json())
.then((user) => console.log(user.name))
.catch((err) => console.error(err));
async/awaitとは
Promiseを同期処理のように書ける構文です。async関数内でawaitを使うとPromiseの解決まで待ち、結果を返り値のように扱えます。
async function loadUser() {
try {
const res = await fetch('/api/user');
const user = await res.json();
console.log(user.name);
} catch (err) {
console.error(err);
}
}
両者の関係
async関数は常にPromiseを返します。await xはx.then(...)と本質的に同じです。async/awaitはPromiseの「糖衣構文(シンタックスシュガー)」と理解すると簡単です。
項目別比較
- 可読性:直列処理はasync/awaitが圧倒的に読みやすい
- エラー処理:async/awaitはtry-catch、Promiseは.catch
- 並列処理:Promise.all/race/allSettled/anyを使う
- 互換性:両者は同じPromiseオブジェクトに依存
並列実行のコツ
// 直列(遅い)
const a = await fetchA();
const b = await fetchB();
// 並列(速い)
const [a, b] = await Promise.all([fetchA(), fetchB()]);
// 失敗を許容して全部待つ
const results = await Promise.allSettled([fetchA(), fetchB()]);
よくある落とし穴
- forEachの中でawaitしても並列化されない(mapで配列にしてPromise.all)
- awaitを忘れるとPromiseのまま返る → 型エラーで気付くケースが多い
- 非同期で発生したエラーは外のtry-catchでは捕捉できない
- トップレベルawaitはESM環境でのみ使える
まとめ
Promiseとasync/awaitは「同じものを別の書き方で表現する」関係です。普段はasync/awaitで読みやすく、並列処理は Promise.all で速くを基本に書けば、ほとんどの非同期処理はシンプルに表現できます。