JavaScript | 非同期処理:Promise 基礎 – then の基本

JavaScript JavaScript
スポンサーリンク

まず then を一言でいうと

then は、
「Promise が“成功したあとにやりたい処理”を登録するための関数」
です。

Promise は「そのうち結果が入る箱」でしたね。
then はその箱に対して、

「中身(成功結果)が決まったら、この関数を呼び出してね」

と約束を登録する場所です。

ここが重要です。
then は「今すぐ実行する」のではなく、
「Promise が fulfilled(成功)になったタイミングで、後から非同期的に実行されるコールバックを登録するもの」
だとイメージしてください。


then の基本形と引数

一番シンプルな書き方

基本形はこうです。

promise.then(onFulfilled);
JavaScript

promise が fulfilled になったとき、
その成功値を引数として onFulfilled が呼び出されます。

例:

const p = Promise.resolve(42);

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

Promise.resolve(42) は、「すでに fulfilled で値 42 を持っている Promise」です。
その Promise に対して then を呼ぶと、
value に 42 が渡ってきます。

成功と失敗の両方を書く形

then は、第2引数に「失敗時の処理」も渡せます。

promise.then(onFulfilled, onRejected);
JavaScript

例:

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

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

こう書けますが、
現代の書き方では「失敗は .catch で書く」ほうが主流です。
初心者のうちは、まずは

成功 → then
失敗 → catch

と分けて覚えるほうが混乱しません。


then が呼ばれるタイミング(ここ大事)

「成功した瞬間」ではなく「イベントループのタイミングで」

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

const p = new Promise((resolve) => {
  resolve("OK");
});

console.log("A");

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

console.log("C");
JavaScript

直感的には「A → B → C」と出そうですが、
実際はこの順です。

A
C
B: OK

理由を分解します。

Promise 作成時に resolve("OK") を呼ぶ
→ Promise はすぐに fulfilled になる
console.log("A") が実行される
p.then(...) で「成功時の処理」が登録される
console.log("C") が実行される
同期処理(このファイルのコード)が終わる
イベントループが「成功済みの Promise の then コールバック」をマイクロタスクとして実行
→ 「B: OK」が出る

つまり、then の中身は「同期コードが終わったあと」に実行されます。

ここが重要です。
then に渡した関数は、「Promise が成功したタイミング」+「今のタスクが一段落したタイミング」で、非同期的に実行される
という点を覚えておいてください。


then は「Promise を返す」こと(チェーンの基本)

then の戻り値が次の処理につながる

then の一番大事なポイントは、
「then そのものが、新しい Promise を返す」
ということです。

const p = Promise.resolve(2);

const p2 = p.then((value) => {
  return value * 3;
});

p2.then((v) => {
  console.log(v); // 6
});
JavaScript

流れはこうです。

最初の Promise p は、2 という値で fulfilled している。
p.then(...) の中で value * 3(=6)を return する。
その return 値 6 が、「p2 という新しい Promise の成功値」になる。
p2.thenv に 6 が渡ってくる。

つまり、then の戻り値として、

普通の値を返せば、「その値で成功した新しい Promise」になる
別の Promise を返せば、「その Promise の結果をそのまま次に流してくれる」

という性質があります。

非同期処理を順番に「直線的に」つなげられる

この性質を使うと、非同期処理をチェーンできます。

fetchUser()          // Promise を返すと仮定
  .then((user) => {
    console.log("ユーザー:", user);
    return fetchPosts(user.id); // これも Promise を返す
  })
  .then((posts) => {
    console.log("投稿:", posts);
    return fetchComments(posts[0].id); // これも Promise
  })
  .then((comments) => {
    console.log("コメント:", comments);
  })
  .catch((err) => {
    console.error("どこかでエラー:", err);
  });
JavaScript

ここでは、

fetchUser の Promise が成功 → 1つ目の then が呼ばれる
その中で fetchPosts の Promise を return → 「次の then の対象」がその Promise に変わる
fetchPosts が成功 → 2つ目の then が呼ばれる
同様に fetchComments を return → 次の then がそれを待つ

というふうに、
時間の流れ(順番に非同期処理したい)が、「then の縦の列」として表現できる ようになります。

ここが重要です。
then が Promise を返すからこそ、「コールバック地獄のネスト」ではなく、「フラットなチェーン」として非同期処理をつなげられる。
これが then の本当の強みです。


then で「値を変換する」イメージ

純粋に値だけを変換するのも普通に使う

then は必ずしも「次の非同期処理」を返さなくてもよくて、
単に値を変換したいだけのときにも使えます。

Promise.resolve(5)
  .then((v) => v * 2)        // 10 に変換
  .then((v) => `値は ${v}`)  // "値は 10" に変換
  .then((msg) => {
    console.log(msg);        // 値は 10
  });
JavaScript

then は、

渡された値を受け取る
何らかの変換をして return する
その return 値が「次の Promise の成功値」になる

という動きをします。

この「値を流しながら変換していく」イメージは、
後で async/await を使うときにもそのまま活きます。


then の第2引数より catch を使うほうがよい理由(さわりだけ)

then(onFulfilled, onRejected) も書けるが…

then の第2引数でエラー処理も書ける、と言いました。

promise.then(
  (value) => { /* 成功時 */ },
  (error) => { /* 失敗時 */ }
);
JavaScript

ですが、これは慣れてきてもあまり使われません。

理由は、

チェーンの途中で「一部だけ」エラーをキャッチしてしまい、
後ろの then にエラーが伝わらなくなることがあるから

です。

catch を末尾につけると「エラーをまとめやすい」

そのため、基本パターンとしては、

promise
  .then(...)
  .then(...)
  .then(...)
  .catch((err) => {
    console.error("どこかで失敗:", err);
  });
JavaScript

という形(成功は全部 then、失敗は最後の catch)
で覚えておくほうが、全体の挙動がシンプルになります。

ここが重要です。
「成功は then」「失敗は catch」という分担で考えると、
Promise チェーンの読み方・書き方がかなりクリアになる。


初心者としての「then の押さえどころ」

最後に、今の段階でしっかり覚えておくべきポイントを整理します。

then は、「Promise が fulfilled(成功)になったときに実行する処理」を登録するためのメソッド。

then に渡したコールバックは、「すぐ」ではなく、「Promise が成功し、かつ今の同期処理が一段落したあと」に非同期的に実行される。

then は必ず新しい Promise を返す。
return した値はその新しい Promise の成功値になり、return した Promise はその結果が次の then に渡される。

これにより、複数の非同期処理を「ネスト」ではなく「チェーン(直線)」としてつなげられる。

エラー処理は基本的に .catch に任せ、「then は成功時だけ」を書く考え方にするとシンプル。

ここが重要です。
then を「成功時の続きを書く場所」+「次の Promise につなぐ橋」として捉えると、Promise 全体のイメージが一気に掴みやすくなります。

おすすめの練習は、

Promise.resolve でスタートする
then を 2〜3 個つないで、値を変換しながら最後に console.log する

といったシンプルなチェーンを書いてみることです。

「値がどう流れ、どこで変換され、どこでログに出るか」を
自分の目で確認できるようになると、
then は「難しい謎の関数」ではなく、
「結果を受けて次の一歩を書くための、素直な道具」 に見えてきます。

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