概要
非同期とは「遅延する」ことではない。
それは**“待つことを制御し、失敗を受け入れ、構造として管理する”**ことに他ならない。
JavaScriptはイベント駆動・シングルスレッド。
非同期処理はアプリの応答性・性能・安全性に直結する。
本稿では、非同期処理を「動作」ではなく「設計」として扱うための戦略を解説する。
1. Promise と async/await の設計原則
✅ async/await は Promise をラップした構造
async function getData() {
const res = await fetch('/api/data');
return await res.json();
}
- ✅
await
は “その場で中断”ではなく、“Promiseの完了を待って次へ” - ✅ 例外は
try-catch
で受ける → 明確な制御構造を持つ
2. 並列 vs 直列:Promise.all / for-await-of の使い分け
✅ 並列に投げる
const [a, b] = await Promise.all([fetchA(), fetchB()]);
✅ 直列で制御する
for (const url of urls) {
const res = await fetch(url);
handle(res);
}
- ✅ 並列:全て同時に開始してまとめて取得
- ✅ 直列:順番や依存がある場合に必須
3. 非同期エラーの捕捉と伝播
async function safeFetch(url) {
try {
const res = await fetch(url);
if (!res.ok) throw new Error('Fetch failed');
return await res.json();
} catch (e) {
return { ok: false, error: e.message };
}
}
- ✅ catch内で構造的なエラー返却を設計
- ✅ 失敗時にも UI が破綻しない構造が必要
4. キャンセル可能な非同期:AbortController の利用
const controller = new AbortController();
fetch('/api', { signal: controller.signal });
setTimeout(() => controller.abort(), 3000);
- ✅ 重複リクエストやコンポーネント破棄時のキャンセルに有効
- ✅ UI操作と連動して非同期のキャンセル設計を導入すべき
5. UIと非同期の境界設計
❌ UI側で直接fetchを実行し状態が管理されない
button.onclick = () => {
fetch('/api'); // 非管理な副作用
};
✅ UI → ロジック層(サービス) → fetch / 処理
async function onClick() {
const data = await fetchUser();
setUser(data);
}
- ✅ 状態と副作用は分離して再現性とテスト性を確保
6. 非同期状態の「今」をUIで表現する
const [loading, setLoading] = useState(false);
const onClick = async () => {
setLoading(true);
try {
const data = await fetchData();
setData(data);
} finally {
setLoading(false);
}
};
- ✅ ローディング・成功・失敗を明示的に管理
- ✅ 非同期の「今の状態」をUIで表現し、体験と同期する
設計判断フロー
① async/await を単に使うだけでなく、責務に応じた関数構成になっているか?
② 並列にできる処理を逐次で書いていないか?
③ エラーが握りつぶされていないか? → catch構造を明示
④ UI破棄時にも非同期処理が生きていないか? → AbortController活用
⑤ UIと非同期処理が分離されているか? → プレゼンテーション層とロジック層を区別
よくあるミスと対策
❌ 複数awaitを逐次で記述 → パフォーマンス劣化
→ ✅ Promise.all
による並列化で高速化
❌ 非同期処理が途中でUIが破棄され、状態更新エラー
→ ✅ AbortController
でキャンセル可能に設計 + cleanup設計
❌ UIが「何が起きているか」を示さずユーザーが混乱
→ ✅ loading, success, error を明示的にstate設計
結語
非同期とは「待つこと」ではない。
それは**“流れを制御し、今を把握し、未来を保証する”構造的な戦略**である。
- 並列と直列を分け
- エラーを受け入れて構造化し
- UIと非同期の責務を分離し
- 状態を「今」としてUIに同期する
JavaScriptにおける非同期設計とは、
“挙動ではなく構造として制御する、再現可能な戦略”である。