JavaScript | 非同期処理:Promise 基礎 – reject の役割

JavaScript JavaScript
スポンサーリンク

まず「reject」のイメージを一言で

reject は、
「この Promise の非同期処理は“失敗”で終わったよ、と確定させるスイッチ」
です。

そして同時に、

「なぜ失敗したのか(エラー情報)はこれだよ」

と、Promise の中に「失敗の理由」をセットする役割 を持っています。

ここが重要です。
reject は単なる「エラー用の return」ではなく、
Promise の状態を pendingrejected に変え、そのエラー情報を catch に渡す“失敗確定ボタン”
だと思ってください。


new Promise と reject の基本的な関係

Promise を「作る側」から見る

Promise を自分で作るとき、基本の形はこうでした。

const p = new Promise((resolve, reject) => {
  // ここで非同期処理を行う
  // 成功なら resolve(...)
  // 失敗なら reject(...)
});
JavaScript

この reject は、
「この Promise はエラーで終わった」と通知するための関数です。

reject(error) を呼ぶと、

Promise の状態:pendingrejected(失敗)になる
Promise の中には error(失敗の理由)が保持される
その Promise にぶら下がっている .catch が、その error を受け取る

という流れになります。

いちばんシンプルな reject の例

const p = new Promise((resolve, reject) => {
  reject(new Error("何かがうまくいかなかった"));
});

p.catch((err) => {
  console.log("エラー:", err.message);
});
JavaScript

ここで reject(new Error(...)) を呼んだ瞬間に、

状態:pendingrejected
エラー情報:Error("何かがうまくいかなかった")

と確定し、.catcherr にそれがそのまま渡されます。

ここが重要です。
resolve が「成功+結果のセット」なら、reject は「失敗+エラー情報のセット」 です。
Promise にとっての「2つの終了ボタン」の片方が reject です。


reject が「状態」をどう変えるか

pending → rejected への一方通行

Promise は作られた直後、必ず pending 状態です。

const p = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(new Error("タイムアウトしました"));
  }, 3000);
});
JavaScript

このコードでは、

Promise 作成直後:pending
3 秒後:reject(...) が呼ばれる → 状態が rejected に変わり、エラー情報が確定する

という流れになります。

いったん rejected になると、その Promise はもう二度と他の状態(fulfilled など)には戻りません。

resolve と reject を両方呼んだらどうなるか

こんなコードを考えてみます。

const p = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(new Error("先に失敗"));
  }, 1000);

  setTimeout(() => {
    resolve("後から成功しようとしても…");
  }, 2000);
});
JavaScript

この場合、「先に呼ばれた方だけ」が有効です。

1 秒後に reject が呼ばれた時点で、
Promise は rejected に確定します。

2 秒後の resolve は、
すでに状態が決まっているので無視されます。

ここが重要です。
reject は「この Promise の最終結果を“失敗”として確定させる、一度きりのボタン」 です。
resolve と同様、後から結果を上書きすることはできません。


reject と catch の関係(エラーの受け渡し)

reject で渡した値は catch の引数に届く

reject に渡した値(多くの場合 Error オブジェクト)は、
その Promise に対する .catch のコールバックの引数として受け取れます。

const p = new Promise((resolve, reject) => {
  reject(new Error("ダメでした"));
});

p.catch((err) => {
  console.log("受け取ったエラー:", err.message); // ダメでした
});
JavaScript

ここで起きているのはこうです。

reject(new Error("ダメでした"))
→ Promise が rejected 状態になり、そのエラー情報を内部に保持
.catch((err) => { ... })err に、そのエラーが渡ってくる

というシンプルな流れです。

then の中でエラーが起きた場合との違いは?

reject を自分で呼ばなくても、
.then のコールバック内でエラーが投げられた場合も、
Promise は自動的に rejected になります。

Promise.resolve(10)
  .then((value) => {
    throw new Error("計算中にエラー");
  })
  .catch((err) => {
    console.log("catch で拾えた:", err.message);
  });
JavaScript

この場合、

.then の中で throw されたエラーが、
内部的に reject と同じ扱いを受けて、
次の .catch に渡されているイメージです。

ここが重要です。
reject は「自分で明示的に失敗を知らせたいとき」に使うボタンであり、
コード中で発生した例外も、内部的には「rejected 状態」として扱われる。
どちらも最終的には .catch で受け止められる
、と理解しておくと楽です。


非同期処理で reject を使う現実的なイメージ

例:擬似的な通信処理を Promise でラップする

通信の成功/失敗を模した例を書いてみます。

function fetchUserMock(shouldFail) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (shouldFail) {
        reject(new Error("ユーザー取得に失敗しました"));
      } else {
        resolve({ id: 1, name: "Taro" });
      }
    }, 1000);
  });
}
JavaScript

使う側はこうです。

fetchUserMock(false)
  .then((user) => {
    console.log("成功:", user);
  })
  .catch((err) => {
    console.log("失敗:", err.message);
  });
JavaScript

ここでの reject の役割は、

「通信や処理がうまくいかなかったときに、その理由を呼び出し元に伝える」

ことです。

reject を呼べば、呼ぶ側は .catch で一箇所にエラー処理を集約 できます。

エラーが起きたらすぐ上に「バケツリレー」できる

Promise の強みは、
.then を何段もつなげても、
どこかで失敗したら .catch 一つで拾えることです。

fetchUserMock(false)
  .then((user) => {
    console.log("ユーザー取得:", user);
    return fetchUserPostsMock(user.id); // ここも Promise を返すとする
  })
  .then((posts) => {
    console.log("投稿取得:", posts);
    return fetchPostCommentsMock(posts[0].id); // これも Promise
  })
  .then((comments) => {
    console.log("コメント取得:", comments);
  })
  .catch((err) => {
    console.error("どこかで失敗:", err.message);
  });
JavaScript

どの段階で reject されたとしても、
最後の .catch でまとめて処理できます。

ここが重要です。
reject は「この非同期処理は失敗」と教えるだけでなく、
「エラーを、後ろに連なった .catch までバケツリレーするための起点」
になっています。


reject と Promise.reject の違い(軽く整理)

Promise.reject は「最初から失敗している Promise」を作るショートカット

resolve と同じく、reject も 2 つの顔があります。

一つ目に、new Promise((resolve, reject) => { ... }) 内の reject
二つ目に、Promise.reject(error) というクラスメソッド。

const p1 = new Promise((resolve, reject) => {
  reject(new Error("エラー"));
});

const p2 = Promise.reject(new Error("エラー"));
JavaScript

どちらも結果としては、

「最初から rejected 状態の Promise」

を作ることになります。

違いとしては、

new Promise 内の reject
→ その Promise の状態を後から「失敗」に変えるボタン

Promise.reject
→ 最初から「失敗している Promise」を即席で作るユーティリティ

という位置づけです。

初心者のうちは、
「Promise.reject は、reject 済みの Promise を手っ取り早く作るもの」
くらいの認識で十分です。


初心者視点での「reject の押さえどころ」

ここまでを、いちばんシンプルに整理します。

Promise は「非同期処理の結果が入る箱」で、最初は pending。

reject(error) を呼ぶと、その Promise は rejected 状態になり、「error」が失敗理由として確定する。

このとき、一度 rejected になった Promise は二度と他の状態には変わらない。

reject に渡したエラー情報は .catch((err) => { ... })err で受け取れる。

reject は「この非同期処理は失敗した」と Promise に知らせる唯一の公式な方法。
(例外を投げても内部的には同じように rejected として扱われる。)

複数の .then をチェーンしていても、どこかで reject されたら .catch 一つでまとめて処理できる。

ここが重要です。
resolve が「成功のゴールボタン」なら、reject は「失敗のゴールボタン」。
非同期処理を Promise 化するときは、「どの条件で resolve するか」「どの条件で reject するか」を最初に設計する

と考えると、かなりスッキリ整理できます。

おすすめは、

成功パターンだけの Promise(resolve だけ)を書いてみる
→ そこに「失敗条件」を追加して reject を呼ぶようにする
.catch がきちんと反応するか確認する

という小さな実験をしてみることです。

自分の手で「成功と失敗の両方」を動かしてみると、
reject が単なるエラーではなく、
「エラーを Promise の世界で“ちゃんと扱えるように変換するためのスイッチ”
だという感覚が掴めてきます。

タイトルとURLをコピーしました