1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

非同期処理のすべて:Promise / async-await / then を使い分けるための設計戦略

Posted at

概要

JavaScriptの非同期処理は、単なる記法の問題ではない。
それは制御フローと責務の設計に深く関わる領域だ。

非同期処理がスパゲッティ化するのは、「Promiseは使ってるけど、なぜそう書くべきかを理解していない」から。
この記事では、非同期処理の成り立ちと、Promise / async-await / then をどのように使い分け、保守性と可読性に優れた非同期ロジックを組むかを実践ベースで解説する。


対象環境

ES6(Promise)以降のJavaScript環境  
Node.js / ブラウザ両対応

非同期処理の背景:なぜ必要なのか?

JavaScriptはシングルスレッドの言語。
UIスレッドをブロックせずにI/O処理やタイマー、ネットワーク通信を扱うために、非同期処理が必要となる。


過去 → 現在への進化

callback hell
↓
Promise(ES6)
↓
async/await(ES2017)

Promiseの本質

const fetchData = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('データ取得成功');
    }, 1000);
  });
};
  • Promise は「未来の値」を扱う箱
  • 状態:pendingfulfilled または rejected
  • メソッド:.then() / .catch() / .finally()

async / await の意味と強み

async function load() {
  try {
    const result = await fetchData();
    console.log(result);
  } catch (error) {
    console.error('失敗:', error);
  }
}
  • await で Promise の解決を待つ(同期的に見える非同期)
  • try / catch でエラー制御ができる
  • 読みやすく、制御構造が明確

thenチェーンとの比較:どう違うのか?

then / catch(従来型)

fetchData()
  .then((res) => console.log(res))
  .catch((err) => console.error(err));

短くて良いが、複雑なロジックではネストが深くなる


実務での使い分け指針

ケース 適した形式 理由
短い一連の処理 .then() その場で完結できる。軽量。
複雑なフロー / 分岐あり async / await 処理の見通しが立ちやすい。
ライブラリ間の橋渡し Promise 明示定義 汎用性が高く、チェーン可能。
外部APIラッパー実装 Promise 状態管理と拡張性に優れる。

エラーハンドリング戦略

✅ 基本的な書き方(async/await)

try {
  const res = await fetch(url);
  if (!res.ok) throw new Error('HTTPエラー');
  const data = await res.json();
} catch (err) {
  console.error(err);
}

✅ 最終保障 .catch() or .finally()

fetch(url)
  .then(process)
  .catch(logError)
  .finally(() => console.log('完了'));

リソース解放・ローダー終了処理などには .finally() が有効


よくあるミスと対策

❌ await をトップレベルで使う(ES2017以前)

const data = await fetch(); // SyntaxError

→ ✅ モジュールスコープでは async function でラップするか、top-level await(ES2022〜)を使う


❌ Promise を忘れて普通の関数にしてしまう

function fetchData() {
  setTimeout(() => {
    return 'データ'; // ← これは返らない
  }, 1000);
}

→ ✅ return new Promise(...) で包むこと


設計視点:非同期処理をどう“責務”として切り分けるか

  • データ取得関数は非同期性を隠蔽しない(呼び出し側が制御できるように)
  • UI操作は同期、状態取得・保存は非同期と明示的に分ける
  • 状態管理(例:Vuex / Redux)と非同期処理の橋渡しは中間層で制御

結語

非同期処理は、JavaScriptの“非同期性”ではなく、構造の問題だ。
どこで待ち、どこで処理し、どこで責任を持つか。
それを意図して設計できてこそ、非同期は制御できる。

動けばいいではなく、読みやすく、拡張可能で、安全な非同期処理を設計する。
その第一歩は、Promise / async-await の違いを「使える」ではなく「選べる」ようになることだ。

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?