2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

JavaScriptにおける非同期制御の設計戦略:Promise, async/await, AbortControllerの実用指針

Posted at

概要

JavaScriptは非同期を本質に持つ言語である。
イベントループ、非同期I/O、UI描画の最適化など、
その基盤は「同期的に見える非同期制御」に支えられている。

本稿では、非同期処理の設計におけるPromise / async/await / AbortControllerの役割と、それらを組み合わせた制御戦略を提示する。
単なる文法ではなく、**「どう制御するか」「どう失敗させるか」「どう管理するか」**の視点で解説していく。


1. Promise設計の基本と型構造

function fetchData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve('data'), 1000);
  });
}
  • ✅ Promiseは「状態管理オブジェクト」:pending → fulfilled / rejected
  • ✅ 複数ステップをthenチェーンで接続可能
  • ❌ 可読性が低下しやすく、エラーハンドリングが錯綜しやすい

2. async/awaitによる同期的表現

async function loadData() {
  try {
    const data = await fetchData();
    console.log(data);
  } catch (e) {
    console.error('Failed:', e);
  }
}
  • Promiseの状態を同期的に待つ構文 → コードの線形性が向上
  • try/catch によるエラー制御が可能
  • ✅ Promiseとの互換性を維持しつつ、コードの明瞭さが増す

3. AbortControllerによるキャンセル可能な非同期制御

const controller = new AbortController();
fetch('/api/data', { signal: controller.signal });

// キャンセル
controller.abort();
  • fetch を中断可能にする標準機能
  • ✅ UIイベントやルーティング変更時の 非同期キャンセル に有効
  • AbortError による専用のハンドリングが必要
try {
  const res = await fetch(url, { signal });
} catch (e) {
  if (e.name === 'AbortError') {
    // 明示的なキャンセル
  } else {
    throw e;
  }
}

4. 非同期エラー設計と再試行戦略

async function withRetry(fn, retries = 3) {
  for (let i = 0; i < retries; i++) {
    try {
      return await fn();
    } catch (e) {
      if (i === retries - 1) throw e;
    }
  }
}
  • ✅ **一時的な障害(ネットワーク、API制限)**に耐性を持たせる
  • ✅ 非同期処理には失敗を前提とした制御構造が必要

5. 非同期の構造化と可視性の確保

モジュールで切り出す

// userService.ts
export async function getUser(id: string, signal?: AbortSignal) {
  const res = await fetch(`/api/users/${id}`, { signal });
  if (!res.ok) throw new Error('Fetch failed');
  return await res.json();
}

UI側で制御

const controller = new AbortController();
getUser('123', controller.signal)
  .then(renderUser)
  .catch(handleError);
  • ✅ 非同期関数は明確な責務でモジュール化
  • ✅ UI側でキャンセル、タイムアウト、再試行を統括

設計判断フロー

① 非同期処理はPromiseであるべきか? → YES → then / async/await に展開

② 処理の順序性が重要か? → async/await で明示

③ ユーザー操作で中断する必要があるか? → AbortControllerを導入

④ エラーは想定されるか? → try/catch + 専用ハンドラ設計

⑤ 再試行・リカバリが必要か? → withRetry等の高階関数で制御

よくあるミスと対策

❌ 非同期処理をtry/catchで囲っていない

→ ✅ async関数内では常にcatchを意識した構造にする


❌ キャンセルできないfetchを多数同時発火

→ ✅ AbortControllerで外部制御可能に設計


❌ UIコンポーネントがアンマウント後にawaitが残ってエラー

→ ✅ useEffect + isMounted or AbortController終了制御


結語

非同期制御は「待つ」だけではない。
それは**“実行の責任とタイミングを設計し、失敗を制御可能にする戦略”**である。

  • async/await で直感的に順序を表現
  • AbortController でキャンセル性を持たせる
  • 高階関数で再試行やタイムアウト制御を汎用化
  • 非同期処理は責務を分離し、構造的に管理する

JavaScriptにおける非同期制御とは、
“動的で不確実なプロセスを予測可能に設計し、ユーザー体験と安定性を支えるアーキテクチャ戦略である。”

2
2
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
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?