JavaScriptにおけるPromise Hell問題

既にご存知のように、プロミスはcallback hell問題を解決するために作られました。 しかし、時が経つにつれ、プロミスもまた複雑なコードを生み出す可能性があることがわかりました。 この問題はアナロジーとしてpromise hellと名付けられました。

コード例を通じてこの問題を見てみましょう。 パラメータを受け取り、そのパラメータに応じて結果を返す関数getSmthがあるとします:

javascriptfunction getSmth(num) {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve(num * num), 1000)
  });
}

この場合、私たちは何らかの有用な操作(例えばサーバーからのデータ取得)をシミュレートしています。 シミュレーションとして、単に数値をパラメータとして渡し、1秒後にその数値の2乗を返します。

では、別の関数内でこの関数getSmthを使ってみましょう:

javascriptfunction func() {
  getSmth(2).then(res => {
    console.log(res); // 4を出力
  });
}

func();

第一の問題

連続する多数のthen構造は、コードの理解を困難にします:

javascriptfunction func(){
  getSmth(2).then(res1 => {
    // 何かを行う
  }).then(res2 => {
    // 何かを行う
  }).then(res3 => {
    // 何かを行う
  }).then(res4 => {
    // 何かを行う
  }).then(res5 => {
    // 何かを行う
  }).then(res6 => {
    // 何かを行う
  });
}

func();

第二の問題

別種類の問題もあります。 今度は、私たちの関数を2回使用し、その結果を合計したいとします。 その結果、以下のようなコードになります:

javascriptfunction func() {
  getSmth(2).then(res1 => {
    getSmth(3).then(res2 => {
      console.log(res1 + res2); // 13を出力
    });
  });
}

func();

もうcallback hellを思い出させますね? もう1回関数呼び出しを追加すると、コードはさらに悪化します:

javascriptfunction func() {
  getSmth(2).then(res1 => {
    getSmth(3).then(res2 => {
      getSmth(4).then(res3 => {
        console.log(res1 + res2 + res3);
      });
    });
  });
}

func();

もちろん、Promise.allを利用することもできます:

javascriptfunction func() {
  Promise.all([getSmth(2), getSmth(3), getSmth(4)]).then(res => {
    console.log(res[0] + res[1] + res[2]);
  });
}

func();

しかし、同じ結果が得られたでしょうか? いいえ! 最初のケースでは、各新しい関数は前のプロミスの完了を待ちますが、 2番目のケースでは、すべてのプロミスが同時に実行されます。 この違いは、次の関数に前の呼び出し結果を渡したい場合に重要になります:

javascriptfunction func() {
  getSmth(2).then(res1 => {
    getSmth(res1).then(res2 => {
      getSmth(res2).then(res3 => {
        console.log(res3);
      });
    });
  });
}

func();