概要
非同期とは「後で起こる」ではなく、
「制御の流れが分断される構造」そのものである。
現代のJavaScriptアプリケーションにおいて、非同期処理は通信・IO・遅延処理など、あらゆるUXのコアとして存在する。
しかし、設計を誤ればそれはバグの温床・読みづらさ・失敗時の不安定な状態を生む。
本稿では、非同期制御の戦略的設計:Promise / async/await の原則、並列 vs 逐次制御、タイムアウト・キャンセル・エラーフローの構造化を包括的に整理する。
1. Promiseの原則と構造
const fetchUser = () =>
new Promise((resolve, reject) => {
setTimeout(() => resolve({ name: 'Toto' }), 1000);
});
- ✅ resolve / reject は一度だけ
- ✅ 状態:pending → fulfilled / rejected
- ✅
.then().catch().finally()
でチェーン制御
2. async/await構文:非同期を同期的に書く
async function loadUser() {
try {
const user = await fetchUser();
console.log(user.name);
} catch (e) {
console.error('取得失敗:', e);
}
}
- ✅
await
はPromiseを待つ - ✅
try/catch
により同期的なエラーフロー設計が可能 - ❌
await
の連発は逐次処理による性能劣化に注意
3. 並列処理 vs 逐次処理の設計判断
❌ 逐次処理:時間がかかる
const a = await fetchA();
const b = await fetchB();
✅ 並列処理:同時に開始
const [a, b] = await Promise.all([fetchA(), fetchB()]);
→ ✅ 依存がなければ並列に、依存があるなら逐次に切り分けて設計
4. タイムアウト・キャンセル設計
function withTimeout(promise, ms) {
const timeout = new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), ms)
);
return Promise.race([promise, timeout]);
}
- ✅ ネットワーク遅延・API停止時のUIロック防止
- ✅ AbortController との併用で実際の通信も中断可能
5. エラーフローの一貫性とUI統合戦略
async function submit() {
try {
showLoading();
const res = await sendData();
showSuccess();
} catch (e) {
showError('送信に失敗しました');
} finally {
hideLoading();
}
}
- ✅ ロジックとUIをイベント的に接続
- ✅
finally
で共通終了処理を保証
6. 複数Promiseの制御構造
const results = await Promise.allSettled([
fetchA(),
fetchB(),
fetchC(),
]);
- ✅
Promise.all
→ いずれか失敗で即reject - ✅
Promise.allSettled
→ 全ての成功/失敗を把握可能 - ✅
Promise.any
→ 最初に成功したものを返す(他は無視)
→ ✅ 目的に応じて制御構造を設計的に選択する
設計判断フロー
① Promiseとasync/awaitを混在していないか? → await優先で構造統一
② 処理が依存してるか? → 逐次か並列かで設計を明確化
③ 失敗時のUI制御はどうなっているか? → try/catch/finallyを必ず設計
④ API遅延に備えているか? → withTimeout / AbortControllerを検討
⑤ 複数非同期の制御方針はあるか? → all / allSettled / any の選定を戦略的に
よくあるミスと対策
❌ 複数 await
を連続で書き、パフォーマンスが悪化
→ ✅ 依存がなければ Promise.all() で並列化
❌ API失敗時に何も表示されない
→ ✅ catchブロック内でUIに対して明示的通知を設計
❌ async関数内でエラーを無視してクラッシュ
→ ✅ try/catch
は常にロジックの中で明示的に設計する
結語
非同期とは「ただ待つ」ことではない。
それは**“制御の流れが見えない世界”を“構造的に可視化する”ための設計行為**である。
- 流れを明確にし
- 失敗を想定し
- 同時性を設計し
- UIとの整合を保証する
JavaScriptにおける非同期設計とは、
「後で何が起こるか」を未来の自分に説明できる構造である。