Проблемата promise hell в JavaScript

Вече знаете, че промисовете бяха създадени, за да решат проблема callback hell. Въпреки това, с времето се оказа, че промисовете също могат да породят сложен код. Този проблем по аналогия беше наречен promise hell.

Нека разгледаме този проблем с примери на код. Нека имаме функция getSmth, която получава параметър и връща резултат в зависимост от този параметър:

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

В този случай ние имитираме някаква полезна операция (например, получаване на данни от сървър). Като имитация ние просто подаваме параметър число и след секунда връщаме квадрата на това число.

Нека сега използваме нашата функция 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();

Втори проблем

Има проблем и от друг вид. Нека сега искаме да използваме нашата функция два пъти, след това да сумираме резултатите. В резултат ще получим такъв код:

javascriptfunction func() {
  getSmth(2).then(res1 => {
    getSmth(3).then(res2 => {
      console.log(res1 + res2); // ще изведе 13
    });
  });
}

func();

Вече напомня на callback hell, нали? Нека добавим одно извикване на функция - кодът ще стане още по-лош:

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();

Но, получихме ли същото? Не! В първия случай всяка следваща функция очаква завършването на предишния промис, а във втория случай - всички промисове се изпълняват едновременно. Тази разлика ще бъде съществена в случая, когато в следващата функция искаме да подадем извикването на предишната:

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

func();