1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

3分で脱・Promise地獄!TypeScript非同期の最速ロードマップ

1
Posted at

TypeScriptで押さえるべき非同期処理のエッセンス

フロントエンド/バックエンド問わず、非同期処理はあらゆるアプリケーションの基盤である。TypeScript では型安全性を担保しつつ、JavaScript と同等の記述性で非同期を扱える。本稿では「コールバック地獄 → Promise → async/await」という歴史的背景を踏まえ、実務で頻出のパターンに絞って要点を整理する。


1. Promise と async/await の役割分担

1.1 Promise で型安全な状態管理

const fetchUser = (id: string): Promise<User> =>
  fetch(`/api/users/${id}`)
    .then(res => res.json() as Promise<User>);
  • Promise<T> は「まだ値が無いが、将来 T が得られる」ことを表す型。
  • 状態は pendingfulfilledrejected の 3 すくみ。型で把握できるメリットは大きい。

1.2 async/await でフローを直列化

async function getUserName(id: string): Promise<string> {
  const user = await fetchUser(id);  // User 型が保持される
  return user.name;
}
  • await により then チェーンの分割が不要になり、読みやすさ向上。
  • 非同期関数は常に Promise<戻り値の型> を返す点に注意。

2. 並行処理と依存関係

2.1 Promise.all で集合的エラー検知

const [profile, posts] = await Promise.all([
  fetchUserProfile(id),
  fetchUserPosts(id),
]);
  • 相互に依存しない呼び出しを 同時実行し、全完了まで待機。
  • いずれかが reject すると即座に catch へバブリング。

2.2 逐次依存は for...of + await

for (const id of userIds) {
  const user = await fetchUser(id);  // 完了順を厳密に保つ
  console.log(user.name);
}
  • forEach + async では意図しない並行実行になるため要注意。

3. エラーハンドリングの鉄則

3.1 try/catch と戻り値ラップ

try {
  const user = await fetchUser(id);
  return { ok: true, data: user } as const;
} catch (error) {
  return { ok: false, error } as const;
}
  • 例外を制御不能な場所まで投げない設計が、再利用性を高める。
  • 戻り値を discriminated union にすると UI 側で型安全に分岐可能。

3.2 予期しない例外を握り潰さない

グローバルで window.addEventListener('unhandledrejection', ...) を仕込み、ログ基盤へ即時転送するのがセオリー。


4. よくある落とし穴

症状 解決策
async void 関数 返り値を無視して例外が握り潰される 必ず Promise<unknown> を返す
競合状態 前回の非同期呼び出し結果が後から到着 AbortController でキャンセル
ブロッキング計算 UI フリーズ Web Worker でオフロード

5. まとめ

  • Promise は状態管理、async/await は可読性向上――この黄金則を守るだけで、大半のバグは防げる。
  • しかし非同期を多用し過ぎると
    1. スタックトレースが分断されデバッグが困難になる
    2. マイクロタスクの過剰生成でイベントループを圧迫し、パフォーマンスが逆に低下する
    3. 依存関係が錯綜し、“どの処理がどこで完了するか” の可視性が失われる
    4. 隠れた競合状態(race condition)が発見しづらくなる
  • 解決策はシンプルである。
    • 同期で書ける箇所は同期で書く(I/O が絡まないロジックはまず同期)。
    • 非同期フローを ドメイン単位で関数分割し、「入力と出力」の責務を明示する。
    • ツールを味方に:TypeScript の型、ESLint の no-floating-promises、そして各種 APM を駆使し、“非同期の健康診断” を継続的に行う。

非同期はアプリを俊敏にする特効薬である一方、無秩序に投与すれば副作用も大きい。
「本当に await が必要か?」 を自問しながら、適量を守って安全に使いこなそう。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?