まず「値の受け渡し」を一言でいうと
Promise の「値の受け渡し」は、
「前の then(または resolve/reject)で決まった値が、次の then / catch にバトンのように渡っていく仕組み」
です。
then の中で return したものが、そのまま「次の then の引数」になっていく。
これが理解できると、Promise のチェーンが一気に読みやすくなります。
ここが重要です。
「then の return が、次の then の引数になる」
この1本のルールを軸に考えると、値の流れは綺麗に整理できます。
基本中の基本:resolve した値 → then の引数
resolve → then の流れ
いちばん根っこの流れから確認します。
const p = new Promise((resolve) => {
resolve(42);
});
p.then((value) => {
console.log("value:", value); // 42
});
JavaScriptこのとき起きていることは、とてもシンプルです。
Promise 作成時:状態は pendingresolve(42) を呼んだ瞬間:状態が fulfilled、「結果値 42」が確定
その Promise に対する .then((value) => { ... }) の value に「42」が渡る
ここでのポイントは、
- 「resolve に渡した値」が
- 「then の引数」としてそのまま届く
この「最初のバトンリレー」を、まずはしっかりイメージしてください。
Promise.resolve でも同じ
Promise.resolve(値) は「最初から成功している Promise」を作るショートカットでしたね。
Promise.resolve("OK").then((v) => {
console.log(v); // "OK"
});
JavaScriptここでも、「resolve された値」がそのまま then の引数として届きます。
then の中の return → 次の then の引数
値の「縦方向のリレー」
次に、「then から次の then への受け渡し」です。
Promise.resolve(1)
.then((v1) => {
console.log("v1:", v1); // 1
return v1 + 2;
})
.then((v2) => {
console.log("v2:", v2); // 3
return v2 * 10;
})
.then((v3) => {
console.log("v3:", v3); // 30
});
JavaScript値の流れを言葉で追うと:
- 一番最初の Promise が「1」で成功する
- 1つ目の then で
v1として1を受け取り、return v1 + 2(3) - その return 値
3が、「次の Promise の成功値」になる - 2つ目の then で
v2として3を受け取り、return v2 * 10(30) - その return 値
30が、「さらに次の Promise の成功値」になる - 3つ目の then で
v3として30を受け取ってログ
というバトンリレーが起きています。
ここが重要です。
各 then の「戻り値」が、次の then の「入力値」になる。
Promise の値の受け渡しは、これがすべての基本です。
return しないとどうなるか(undefined が渡る)
初心者がよくやるミスも触れておきます。
Promise.resolve(1)
.then((v1) => {
console.log("v1:", v1);
// 何も return していない
})
.then((v2) => {
console.log("v2:", v2); // undefined
});
JavaScript1つ目の then の中で何も return していません。
JavaScript では「何も return しない」は「undefined を返す」と同じです。
つまり、
1つ目の then の戻り値は undefined
→ 「undefined で成功した Promise」が次に渡される
→ 2つ目の then の v2 は undefined
という流れになります。
非同期処理をチェーンしたいときは、
「then の最後で必ず return する」というクセをつけておくと、安全で分かりやすいです。
then から「Promise を返す」ときの受け渡し
Promise を返したら、その結果が次に渡る
値だけでなく、「別の Promise」を return することもできます。
これが非同期チェーンの核になります。
function doubleAsync(x) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(x * 2);
}, 500);
});
}
Promise.resolve(5)
.then((v1) => {
console.log("v1:", v1); // 5
return doubleAsync(v1); // ここで Promise を return
})
.then((v2) => {
console.log("v2:", v2); // 10 (500ms 後)
});
JavaScriptここで起きていることを分解すると:
- 最初の Promise が「5」で成功
- 1つ目の then で 5 を受け取り、
doubleAsync(5)(=「500ms 後に 10 で成功する Promise」)を return - チェーンは「その Promise が落ち着く(fulfilled or rejected)」まで待つ
doubleAsyncの Promise が10で成功したタイミングで、次の then がv2 = 10を受け取る
ここが重要です。
then の戻り値が「Promise」だった場合、その Promise の「中身の結果」が次の then に渡される。
自動的に「待って → 結果だけ渡してくれる」という動きになっています。
値を返すときと Promise を返すときの違いのイメージ
まとめると:
- 普通の値を return
→ その値を持った「成功済み Promise」が次に渡る - Promise を return
→ その Promise が解決されるまで待ち、その結果値が次に渡る
どちらにしても、
「次の then は“値”を受け取るだけでよい」
というのが使いやすさのポイントです。
catch への値の受け渡し
reject された値 → 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 に渡るのと同じように、
「失敗理由」が catch に渡っていきます。
then 内で throw したエラーも catch に渡る
もう一段見てみます。
Promise.resolve(10)
.then((v) => {
console.log("v:", v);
throw new Error("ここでエラー");
})
.catch((err) => {
console.log("catch:", err.message); // ここでエラー
});
JavaScriptthen の中で例外を投げていますが、
このエラーは内部的に「rejected 状態」として扱われ、
次の catch に渡されます。
つまり、
「正常な return」は次の then の引数へ
「throw されたエラー」や reject の値は次の catch の引数へ
というふうに、
成功・失敗どちらも「値のバトンリレー」が起きている と考えると整理しやすいです。
finally と値の受け渡し(軽く触れる)
finally 自体は値を「受け取らない」
finally は「後片付け専用」で、引数は受け取りません。
Promise.resolve("OK")
.finally(() => {
console.log("必ず実行されるが、値は受け取らない");
})
.then((value) => {
console.log("value:", value); // "OK"
});
JavaScriptfinally のコールバックには value や error は渡ってきません。
(引数を書いても常に undefined です。)
でも「成功値 / 失敗理由」は基本的にそのまま次へ流れる
上の例では、
元の Promise が "OK" で成功しているので、finally のあとに続く then の value は "OK" のままです。
失敗側も同様です。
Promise.reject(new Error("NG"))
.finally(() => {
console.log("片付け");
})
.catch((err) => {
console.log("エラー:", err.message); // NG
});
JavaScript元が reject なら、その失敗理由は finally を挟んでも catch に渡ります。
原則として、
- finally の中で特別なエラーを投げない限り
- 元の「成功値 / 失敗理由」はそのまま次の then / catch に受け渡される
と覚えておけば十分です。
よくある「値の受け渡し」で混乱するポイント
then の中の非同期を「return し忘れる」問題
さっき少し触れましたが、もう一度強調しておきます。
fetchUser()
.then((user) => {
// 悪い例
fetchPosts(user.id); // return していない
})
.then((posts) => {
console.log(posts); // ここには undefined が来る(もしくは想定外のタイミング)
});
JavaScriptこれだと、
1つ目の then の戻り値は undefined(= 何も返していない)
→ 次の then の引数も undefined
になってしまい、
fetchPosts の結果はチェーンに繋がりません。
正しくはこうです。
fetchUser()
.then((user) => {
return fetchPosts(user.id); // Promise を return
})
.then((posts) => {
console.log(posts); // ここで投稿一覧が取れる
});
JavaScriptここが本当に重要です。
「then の中で新しい Promise を作ったら、必ず return する」
これが、値を正しく“次に受け渡す”ための最低ルールです。
複数の値を渡したいときは「1つのオブジェクト」にまとめる
Promise の then は、基本的に「1つの値」しか渡せません。
(関数に複数引数を渡したいときは、自分でオブジェクトや配列に詰めます。)
例えば、「ユーザーと投稿を両方次に渡したい」場合。
fetchUser()
.then((user) => {
return fetchPosts(user.id).then((posts) => {
return { user, posts }; // 1つのオブジェクトにまとめる
});
})
.then(({ user, posts }) => {
console.log("user:", user);
console.log("posts:", posts);
});
JavaScriptまたは、フラットに書くなら:
fetchUser()
.then((user) => {
return fetchPosts(user.id).then((posts) => ({ user, posts }));
})
.then(({ user, posts }) => {
console.log(user, posts);
});
JavaScript「複数の値を受け渡ししたいときは、オブジェクトか配列で包んで 1 つにする」
というのも覚えておくと役に立ちます。
初心者向け「値の受け渡し」まとめ
Promise の値の受け渡しを、いちばん大事なポイントだけに絞るとこうなります。
resolve(値) で決めた値は、その Promise の then の引数に渡る。
then の中で return した値が、そのまま「次の then の引数」になる。
then の中で Promise を return した場合、その Promise が解決されたときの「中身の値」が次の then に渡る(チェーン処理の核)。
何も return しないと、その then の戻り値は undefined になり、次の then の引数も undefined になる。
reject(エラー) や then 内の throw で発生した失敗理由は、catch の引数に渡る。
複数の値を渡したいときは、自分でオブジェクトや配列にまとめて 1 つの値として受け渡しする。
ここが重要です。
Promise のコードを読むときは、「今この then は何を return していて、その値が次の then / catch のどこに届いているのか?」を意識して追うこと。
この“値の流れ”を追う癖がつくと、非同期チェーンが一気にクリアに見えるようになります。
おすすめの練習としては、
Promise.resolve(1) からスタートして
- then で 2 倍
- then で +3
- then で文字列に変換
といったチェーンを自分で書き、
「どこで何を return して、次の then の引数がどう変わっていくか」を
コンソールログで確認してみてください。
それを一度体感すると、
Promise の「値の受け渡し」は、もう怖いものではなく、
ただのキレイな“バトンリレー” に見えてくるはずです。
