JavaScript | 非同期処理:Promise 基礎 – Promise の状態(pending / fulfilled / rejected)

JavaScript JavaScript
スポンサーリンク

Promise の状態を一言でイメージする

Promise は
「そのうち結果が入る箱」 で、
その箱には常にどれか 1 つの「状態」がくっついています。

pending(保留中)
fulfilled(成功)
rejected(失敗)

Promise はこの 3 つのどれかに必ずいます。
そして、いったん fulfilled か rejected になると、それ以上は二度と変わりません。

ここがとても重要です。
Promise は「途中のグラグラした状態」から「成功 or 失敗のどちらかに確定する」一方通行のオブジェクト です。


状態1:pending(保留中)とは何か

まだ結果が分かっていない「待ち」の状態

Promise を new Promise(...) で作った瞬間、状態は必ず pending です。

const p = new Promise((resolve, reject) => {
  // ここで非同期の準備をする
});
JavaScript

この瞬間、
「まだ成功でも失敗でもない。結果待ち」
という状態になっています。

例えば setTimeoutfetch を中で使っている場合、

タイマーが終わるまで
サーバーから返事がくるまで

のあいだ、Promise はずっと pending です。

pending は「fulfilled でも rejected でもない」という意味

難しく考えなくて大丈夫です。

pending =「まだ決まっていない」
fulfilled でも rejected でもない中間状態。

このタイミングで .then.catch を登録しておくと、
「結果が決まったときに呼び出してね」と予約している ことになります。


状態2:fulfilled(成功)とは何か

非同期処理が「うまくいった」状態

Promise の中の処理がうまくいったとき、
作った側が resolve(値) を呼びます。

const p = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("成功したよ");
  }, 1000);
});
JavaScript

1 秒後に resolve("成功したよ") が呼ばれた瞬間、
Promise は pending から fulfilled に変わり、
その値として “成功したよ” を持つようになります。

このとき、

「操作が正常に完了した」
「結果の値が確定した」

という意味で、「成功状態」になっています。

fulfilled になると、then が呼ばれる

上の Promise に .then をつけるとこうなります。

p.then((value) => {
  console.log("成功時の値:", value);
});
JavaScript

流れとしては、

Promise 作成 → まだ pending
1 秒後に resolve → 状態が fulfilled、value が決まる
イベントループが .then に登録されたコールバックを呼び出す

という順番です。

ここが重要です。
fulfilled は、「結果が決まった Promise」。
then は、その「決まった結果を受け取るための場所」
と理解してください。


状態3:rejected(失敗)とは何か

非同期処理が「失敗した」状態

何らかの理由で処理がうまくいかなかったとき、
Promise の作り手は reject(エラー) を呼びます。

const p = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(new Error("何かがおかしい"));
  }, 1000);
});
JavaScript

1 秒後に reject(...) が呼ばれた瞬間、
Promise の状態は pending から rejected に変わり、
「失敗の理由(Error オブジェクトなど)」を保持します。

rejected になると、catch が呼ばれる

これに .catch をつけてみます。

p.catch((error) => {
  console.log("失敗しました:", error.message);
});
JavaScript

流れは、

Promise 作成 → pending
1 秒後に reject → 状態が rejected、失敗理由が決まる
イベントループが .catch に登録されたコールバックを呼び出す

です。

ここが重要です。
rejected は、「エラー発生で終わった Promise」。
catch は、そのエラーを一箇所で受け止めるための場所
です。


一度決まったら戻らない(状態が「確定」する)

fulfilled と rejected は「片道切符」

Promise には 3 つの状態がありますが、
「行ったり来たり」することはありません。

作成直後は必ず pending。
そこから、

成功したら fulfilled
失敗したら rejected

のどちらかに「一度だけ」遷移します。

一度 fulfilled か rejected になった Promise は、
そこから別の状態に変わることはありません。

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

例えば、こんなコードを見てみます。

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

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

ここでは 1 秒後に reject、2 秒後に resolve を呼んでいますが、
先に呼ばれたほうだけが効きます

1 秒後:pending → rejected に変わる
2 秒後:すでに rejected なので、後からの resolve は無視される

つまり、

「一度決まった結果は、あとから上書きできない」

というルールになっています。

ここが重要です。
Promise の状態は「pending →(成功 or 失敗のどちらか)」で終わり。
二度と揺れ動かない「確定した結果」である

という感覚を持ってください。


状態と「使う側のコード」の関係をイメージする

pending の間に then / catch を登録する

次のコードを見てください。

const p = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("OK");
  }, 1000);
});

console.log("A");

p.then((value) => {
  console.log("成功:", value);
}).catch((error) => {
  console.log("失敗:", error);
});

console.log("B");
JavaScript

時間の流れとしては、

Promise 作成 → pending
“A” を出力
then / catch を登録(この時点でもまだ pending)
“B” を出力
1 秒後に resolve → fulfilled へ
そのタイミングで then のコールバックが呼ばれる

なので出力順は、

A
B
成功: OK

になります。

ここが重要です。
「Promise の状態の変化(pending → fulfilled/rejected)」と「then/catch のコールバック実行」はセット です。
状態が変わったタイミングで、予約しておいた処理が動きます。

すでに状態が決まっていても then は使える

Promise の面白いところは、
「もう fulfilled 済みの Promise」に対しても .then を呼べることです。

const p = Promise.resolve(42); // すでに fulfilled の Promise を作るユーティリティ

p.then((value) => {
  console.log("value:", value); // 42
});
JavaScript

この場合も、

内部的には「すでに fulfilled だな」と分かっている
.then のコールバックを「マイクロタスク」として予約
→ 現在の同期処理が終わった時点でコールバックを実行

という形になっています。

つまり、

「状態が決まっている Promise」も
「これから決まる Promise」も
同じように .then / .catch で扱える

というのが、Promise の気持ちよさです。


初心者として「状態」をどう覚えておくと楽か

ここまでを、できるだけシンプルにまとめます。

Promise は「非同期処理の結果が入る箱」。
作った直後は必ず pending(保留)。

処理がうまくいったら resolve が呼ばれて fulfilled(成功)に変わり、「結果の値」が確定する。

処理が失敗したら reject が呼ばれて rejected(失敗)に変わり、「エラー情報」が確定する。

一度 fulfilled か rejected になった Promise は、二度と別の状態にはならない(片道切符)。

then は 「fulfilled になったときに呼びたい処理」を登録する場所。
catch は 「rejected になったときに呼びたい処理」を登録する場所。

これだけ頭に入っていれば、
Promise の状態に関する基礎はしっかり押さえられています。

あとは実際に、

少し待ってから resolve する Promise
少し待ってから reject する Promise

を自分で作り、console.log(promise) して
「最初は pending → しばらくして fulfilled / rejected」
と変わっていくのを確認してみると、
状態のイメージがグッと掴みやすくなります。

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