Promise hell -ongelman JavaScriptissä

Tiedät jo, että promiset luotiin ratkaisemaan callback hell -ongelma. Kuitenkin ajan myötä kävi ilmi, että promiset voivat myös synnyttää monimutkaista koodia. Tätä ongelmaa kutsutaan analogisesti nimellä promise hell.

Tarkastellaan tätä ongelmaa koodiesimerkkien avulla. Oletetaan, että meillä on funktio getSmth, joka saa parametrin ja palauttaa tuloksen riippuen tästä parametristä:

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

Tässä tapauksessa imitoimme jotain hyödyllistä toimintoa (esimerkiksi tietojen hakemista palvelimelta). Imitaationa välitämme parametrina yksinkertaisesti luvun ja palautamme sekunnin kuluttua sen neliön.

Hyödynnetään nyt funktiotamme getSmth toisen funktion sisällä:

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

func();

Ensimmäinen ongelma

Useat peräkkäiset then -rakenteet vaikeuttavat koodin ymmärtämistä:

javascriptfunction func(){
  getSmth(2).then(res1 => {
    // teemme jotain
  }).then(res2 => {
    // teemme jotain
  }).then(res3 => {
    // teemme jotain
  }).then(res4 => {
    // teemme jotain
  }).then(res5 => {
    // teemme jotain
  }).then(res6 => {
    // teemme jotain
  });
}

func();

Toinen ongelma

On olemassa toisenlainen ongelma. Oletetaan nyt, että haluamme hyödyntää funktiotamme kaksi kertaa ja laskea tulokset yhteen. Tuloksena saamme tällaisen koodin:

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

func();

Muistuttaa jo callback helliä, eikö vain? Lisätään vielä yksi funktiokutsu - koodista tulee vielä huonompi:

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

func();

Voisimme tietysti hyödyntää Promise.all:

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

func();

Mutta saimmeko saman tuloksen? Ei! Ensimmäisessä tapauksessa jokainen uusi funktio odottaa edellisen promisen valmistumista, kun taas toisessa tapauksessa - kaikki promiset suoritetaan samanaikaisesti. Tämä ero on merkittävä silloin, kun haluamme välittää seuraavaan funktioon edellisen funktion tuloksen:

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

func();