JavaScript 逆引き集 | Promise 基本生成

JavaScript JavaScript
スポンサーリンク

Promise の基本生成 — new Promise((resolve, reject) => {...})

Promiseは「未来のある時点で値が返ることを約束する」オブジェクト。非同期処理の結果と状態を表し、成功・失敗・処理中をわかりやすく扱えます。


概念と状態

  • Promiseの役割: 非同期処理の「結果」と「進行状態」を一つのオブジェクトにまとめる仕組み。
  • 3つの状態: Pending(処理中)→ Fulfilled(成功)→ Rejected(失敗)。一度FulfilledかRejectedになると、それ以降は変わらない。

基本の作り方と使い方

// 1) Promiseを自分で生成する
const p = new Promise((resolve, reject) => {
  // 非同期処理を書く(例: タイマーで疑似非同期)
  setTimeout(() => {
    const ok = Math.random() > 0.3; // 70%で成功
    if (ok) resolve("成功しました");
    else reject(new Error("失敗しました"));
  }, 500);
});

// 2) 結果の受け取り
p.then(result => {
  console.log("結果:", result);       // 成功時
}).catch(err => {
  console.error("エラー:", err.message); // 失敗時
}).finally(() => {
  console.log("必ず呼ばれるクリーンアップ");
});
JavaScript
  • executor関数: new Promise((resolve, reject) => {...}) の中で非同期処理を行い、成功なら resolve(value)、失敗なら reject(error) を呼ぶ。
  • then/catch/finally: 成功時・失敗時・常時実行のハンドラを登録して連鎖できる。

まずは「コールバック地獄」を避ける

従来のコールバックは入れ子が深くなりがち。Promiseは「関数が返すオブジェクトに対してハンドラを登録」する設計で、処理の連鎖とエラーハンドリングがシンプルになります。

// コールバック風(入れ子が増えやすい)
setTimeout(() => {
  setTimeout(() => {
    console.log("二段目");
  }, 500);
}, 500);

// Promise風(直線的につなげる)
new Promise(res => setTimeout(res, 500))
  .then(() => new Promise(res => setTimeout(res, 500)))
  .then(() => console.log("二段目"));
JavaScript

すぐ使えるテンプレート集

疑似API呼び出しテンプレート

function fetchUser(id) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (id > 0) resolve({ id, name: "Aki" });
      else reject(new Error("Invalid id"));
    }, 300);
  });
}

fetchUser(1)
  .then(user => console.log("user:", user))
  .catch(err => console.error(err));
JavaScript

チェーンで段階処理(加工→検証→保存)

new Promise(res => res({ id: 1, name: "Aki" }))
  .then(u => ({ ...u, name: u.name.toUpperCase() }))
  .then(u => {
    if (!u.name) throw new Error("No name");
    return u;
  })
  .then(u => console.log("保存:", u))
  .catch(err => console.error("失敗:", err.message));
JavaScript

エラーハンドリングのポイント(catchは末尾に)

doTask() // Promiseを返す仮の関数
  .then(r => step2(r))     // ここでエラーになればcatchへ
  .then(r => step3(r))
  .catch(err => {
    console.error("どこで失敗してもここで受ける:", err);
  })
  .finally(() => {
    console.log("リソース解放など");
  });
JavaScript
  • エラーはチェーンをショートサーキットして catch に流れるため、末尾に一箇所置くと見通しが良い。

Promiseの静的メソッド(併用すると便利)

// 即座に成功/失敗のPromiseを作る
Promise.resolve(123).then(v => console.log(v));      // 123
Promise.reject(new Error("x")).catch(e => console.log(e.message)); // x

// 複数の非同期を並行
const p1 = new Promise(res => setTimeout(() => res("A"), 300));
const p2 = new Promise(res => setTimeout(() => res("B"), 200));

Promise.all([p1, p2]) // 全成功で配列が返る、1つでも失敗でreject
  .then(([a, b]) => console.log(a, b))
  .catch(console.error);

Promise.race([p1, p2]) // 最初に完了した結果だけ得る
  .then(v => console.log("race:", v));

Promise.allSettled([p1, p2]) // 成否問わず全結果をまとめる
  .then(results => console.log(results));
JavaScript
  • then/catch/finallyの連鎖や、all/race/allSettled/resolve/reject の活用で表現力が上がる。

async/awaitとの関係(読みやすい糖衣)

async 関数は必ずPromiseを返し、await は「Promiseの結果が来るまで待つ」記法。中身はPromiseだが、直線的な書き味で可読性が高い。

function delay(ms) {
  return new Promise(res => setTimeout(res, ms));
}

async function run() {
  try {
    await delay(300);
    const user = await fetchUser(1); // 上で定義した関数を再利用
    console.log("取得:", user);
  } catch (e) {
    console.error("失敗:", e.message);
  } finally {
    console.log("完了");
  }
}

run();
JavaScript

よくある落とし穴と対策

  • executorで例外を投げると自動reject: try/catchで包むか、例外はrejectに渡す。
  • resolve後にさらにresolve/rejectしても無効: 状態は一度確定したら不変。
  • new Promiseの乱用は避ける: 既にPromiseを返すAPIをさらにラップしない(「Promiseコンストラクタアンチパターン」)。
  • エラーを飲み込まない: catchを必ずどこかで置く。チェーンの末尾または上位で集約。

練習問題(手を動かして覚える)

// 1) 基本生成と成功・失敗の2パターン
const pOK = new Promise(res => res("OK"));
const pNG = new Promise((_, rej) => rej(new Error("NG")));
pOK.then(console.log);
pNG.catch(e => console.log(e.message));

// 2) チェーンで2倍→文字列化→出力
new Promise(res => res(10))
  .then(x => x * 2)
  .then(x => String(x))
  .then(console.log); // "20"

// 3) 並行処理:3つの遅延どれが最初?
const d = ms => new Promise(res => setTimeout(() => res(ms), ms));
Promise.race([d(300), d(100), d(200)]).then(ms => console.log("最速:", ms));

// 4) async/awaitで例外処理
async function mayFail(ok) {
  if (!ok) throw new Error("Oops");
  return "done";
}
(async () => {
  try {
    console.log(await mayFail(true));
    console.log(await mayFail(false));
  } catch (e) {
    console.error("catch:", e.message);
  }
})();
JavaScript

直感的な指針

  • 「非同期の結果を一つのオブジェクトにまとめ、成功・失敗を明示する」のがPromise。
  • new Promise((resolve, reject) => {...}) の中で成功ならresolve、失敗ならreject。受け取りは then/catch/finally で連鎖。
  • 既存のPromiseを返すAPIはそのまま使い、async/awaitで読みやすく書く。
タイトルとURLをコピーしました