Chapter 17

Promise chain で値を繋ぐ

PADAone🐕
PADAone🐕
2023.07.22に更新

このチャプターについて

前のチャプターを通して、Promise chain の基本的な動きが分かったと思います。ここからは Promise chain において値をつないでいく処理を考えてみたいと思います。

次のチェーンに値を繋ぐ

then() メソッドの引数のコールバックには入力として前の then() メソッドのコールバック内にて return した値を渡すことができます。

先程のコードを更に改造して、実際にテストしてみます。再び実行の順番を予想してみてください。

// returnPromiseByFuncArg2AddChainValue.js
console.log("🦖 [A] MAINLINE(Start): Sync");

const returnPromise = (resolvedValue, order) => {
  return new Promise((resolve) => {
    console.log(`👻 ${order} Sync`);
    resolve(resolvedValue);
  });
};

returnPromise("🐵 1st Promise", "[B]")
  .then((value1) => {
    console.log("👦 [C]", value1);
    return "value from 1st then";
  })
  .then((value2) => {
    console.log("👦 [D]", value2);
    // return "value from 2nd then";
  })
  .then((value3) => {
    console.log("👦 [E]", value3);
  });

returnPromise("🐵 2nd Promise", "[F]")
  .then((value1) => {
    console.log("👦 [G]", value1);
    return "value from 1st then";
  })
  .then((value2) => {
    console.log("👦 [H]", value2);
    return "value from 2nd then";
  })
  .then((value3) => {
    console.log("👦 [I]", value3);
  });

console.log("🦖 [J] MAINLINE(End): Sync");
答え

答えは以下のようになります。

❯ deno run returnPromiseByFuncArg2AddChainValue.js
🦖 [A] MAINLINE(Start): Sync
👻 [B] Sync
👻 [F] Sync
🦖 [J] MAINLINE(End): Sync
👦 [C] 🐵 1st Promise
👦 [G] 🐵 2nd Promise
👦 [D] value from 1st then
👦 [H] value from 1st then
👦 [E] undefined
👦 [I] value from 2nd then

アルファベットに数字をつけてみると分かりやすくなります。

// returnPromiseByFuncArg2AddChainValue-num.js
console.log("🦖 [1] MAINLINE(Start): Sync");

const returnPromise = (resolvedValue, order) => {
  return new Promise((resolve) => {
    console.log(`👻 ${order} Sync`);
    resolve(resolvedValue);
  });
};

returnPromise("🐵 1st Promise", "[2]")
  .then((value1) => {
    console.log("👦 [5]", value1);
    return "value from 1st then";
  })
  .then((value2) => {
    console.log("👦 [7]", value2);
    // return "value from 2nd then";
  })
  .then((value3) => {
    console.log("👦 [9]", value3);
  });

returnPromise("🐵 2nd Promise", "[3]")
  .then((value1) => {
    console.log("👦 [6]", value1);
    return "value from 1st then";
  })
  .then((value2) => {
    console.log("👦 [8]", value2);
    return "value from 2nd then";
  })
  .then((value3) => {
    console.log("👦 [10]", value3);
  });

console.log("🦖 [4] MAINLINE(End): Sync");

動きは前のコードと同じなので解説はしません。JS Visualizer 9000 で可視化したものは以下です。

  • returnPromiseByFuncArg2AddChainValue.js
  • ⚠️ 注意: JS Visualizer ではグローバルコンテキストは可視化されないので最初のマイクロタスク実行のタイミングについて誤解しないように注意してください

ポイントとしては、return 文をコメントアウトしてある then() コールバックの次の then() コールバックでは、渡されるはずの値がないので undefined となっている点です。何も return しない場合には次の then() メソッドのコールバックの入力値は undefined となるので注意してください。

チェーンの最後まで値を繋ぐ

Promise chain で「値を繋ぐ」ことが理解しづらい場合には次のコードを考えてみます。このコードでは、returnPromise() 関数の第一引数として渡した文字列 "1st Promise" を Promise chain において then() メソッドのコールバックで毎回 return することで最後まで値を繋げています。

chainValue.js
// chainValue.js
console.log("🦖 [1] MAINLINE(Start): Sync");

const returnPromise = (resolvedValue, order) => {
  return new Promise((resolve) => {
    console.log(`👻 ${order} Sync`);
    resolve(resolvedValue);
  });
};

// 文字列 "🐵 1st Promise" で解決された後にその値を最後まで連鎖させる
returnPromise("🐵 1st Promise", "[2]")
  .then((value1) => {
    console.log("👦 [4]", value1); // 🐵 1st Promise
    return value1;
  })
  .then((value2) => {
    console.log("👦 [5]", value2); // 🐵 1st Promise
    return value2;
  })
  .then((value3) => {
    console.log("👦 [6]", value3); // 🐵 1st Promise
    return value3;
  })
  .then((value4) => {
    console.log("👦 [7]", value4); // 🐵 1st Promise
  });

console.log("🦖 [3] MAINLINE(End): Sync");

これを実行すると以下の出力を得ます。

❯ deno run chainValue.js
🦖 [1] MAINLINE(Start): Sync
👻 [2] Sync
🦖 [3] MAINLINE(End): Sync
👦 [4] 🐵 1st Promise
👦 [5] 🐵 1st Promise
👦 [6] 🐵 1st Promise
👦 [7] 🐵 1st Promise

value1 → value2 → value3 → value4 というように値 "1st Promise" が最後まで連鎖できていることに注目してください。

then() メソッドのコールバック関数の引数に数値をつけたのは考えやすくするためで別に同じ value でもよいです。

// chainValueName.js
console.log("🦖 [1] MAINLINE(Start): Sync");

const returnPromise = (resolvedValue, order) => {
  return new Promise((resolve) => {
    console.log(`👻 ${order} Sync`);
    resolve(resolvedValue);
  });
};

// 文字列 "🐵 1st Promise" で解決された後にその値を最後まで連鎖させる
returnPromise("🐵 1st Promise", "[2]")
  .then((value) => {
    console.log("👦 [4]", value); // 🐵 1st Promise
    return value;
  })
  .then((value) => {
    console.log("👦 [5]", value); // 🐵 1st Promise
    return value;
  })
  .then((value) => {
    console.log("👦 [6]", value); // 🐵 1st Promise
    return value;
  })
  .then((value) => {
    console.log("👦 [7]", value); // 🐵 1st Promise
  });

console.log("🦖 [3] MAINLINE(End): Sync");