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この瞬間、
「まだ成功でも失敗でもない。結果待ち」
という状態になっています。
例えば setTimeout や fetch を中で使っている場合、
タイマーが終わるまで
サーバーから返事がくるまで
のあいだ、Promise はずっと pending です。
pending は「fulfilled でも rejected でもない」という意味
難しく考えなくて大丈夫です。
pending =「まだ決まっていない」
fulfilled でも rejected でもない中間状態。
このタイミングで .then や .catch を登録しておくと、
「結果が決まったときに呼び出してね」と予約している ことになります。
状態2:fulfilled(成功)とは何か
非同期処理が「うまくいった」状態
Promise の中の処理がうまくいったとき、
作った側が resolve(値) を呼びます。
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("成功したよ");
}, 1000);
});
JavaScript1 秒後に 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);
});
JavaScript1 秒後に 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」
と変わっていくのを確認してみると、
状態のイメージがグッと掴みやすくなります。
