JavaScript | 非同期処理:Promise 基礎 – 値の受け渡し

JavaScript JavaScript
スポンサーリンク

まず「値の受け渡し」を一言でいうと

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 作成時:状態は pending
resolve(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

値の流れを言葉で追うと:

  1. 一番最初の Promise が「1」で成功する
  2. 1つ目の then で v1 として 1 を受け取り、return v1 + 2(3)
  3. その return 値 3 が、「次の Promise の成功値」になる
  4. 2つ目の then で v2 として 3 を受け取り、return v2 * 10(30)
  5. その return 値 30 が、「さらに次の Promise の成功値」になる
  6. 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
  });
JavaScript

1つ目の then の中で何も return していません。
JavaScript では「何も return しない」は「undefined を返す」と同じです。

つまり、

1つ目の then の戻り値は undefined
→ 「undefined で成功した Promise」が次に渡される
→ 2つ目の then の v2undefined

という流れになります。

非同期処理をチェーンしたいときは、
「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

ここで起きていることを分解すると:

  1. 最初の Promise が「5」で成功
  2. 1つ目の then で 5 を受け取り、doubleAsync(5)(=「500ms 後に 10 で成功する Promise」)を return
  3. チェーンは「その Promise が落ち着く(fulfilled or rejected)」まで待つ
  4. 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); // ここでエラー
  });
JavaScript

then の中で例外を投げていますが、
このエラーは内部的に「rejected 状態」として扱われ、
次の catch に渡されます。

つまり、

「正常な return」は次の then の引数へ
「throw されたエラー」や reject の値は次の catch の引数へ

というふうに、
成功・失敗どちらも「値のバトンリレー」が起きている と考えると整理しやすいです。


finally と値の受け渡し(軽く触れる)

finally 自体は値を「受け取らない」

finally は「後片付け専用」で、引数は受け取りません。

Promise.resolve("OK")
  .finally(() => {
    console.log("必ず実行されるが、値は受け取らない");
  })
  .then((value) => {
    console.log("value:", value); // "OK"
  });
JavaScript

finally のコールバックには valueerror は渡ってきません。
(引数を書いても常に undefined です。)

でも「成功値 / 失敗理由」は基本的にそのまま次へ流れる

上の例では、
元の Promise が "OK" で成功しているので、
finally のあとに続く thenvalue"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 の「値の受け渡し」は、もう怖いものではなく、
ただのキレイな“バトンリレー” に見えてくるはずです。

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