Какво е Promise и защо трябва да ги използваме?

Ако сте програмирали в JavaScript за известно време, вероятно сте изпитали справедливия си дял от обратни извиквания и сте въвели „callback hell“. Има различен подход към асинхронния програмен поток, който можем да използваме, наречен Promises.

MDN гласи: Обектът Promise представлява евентуалното завършване (или неуспех) на асинхронна операция и произтичащата от нея стойност.

Обещанията са много по-интуитивни, защото можем да използваме нашите обичайни езикови функции с термини като „връщане“ и „хвърляне“, за да обработваме стойност и обработка на грешки в синхронни потоци. Обещанията представляват стойност, с която можем да се справим в даден момент в бъдещето, и ни дават гаранции за тази бъдеща стойност! Две гаранции са, че обещанията са неизменни и ние ще получим тази обработена крайна стойност.

Има ли нещо, което Promise не може да направи?

Е, не можете да отмените обещание. Веднъж създаден, той ще започне да се изпълнява. Също така не можете да проследите статуса на обещание, т.е. дали е чакащо, изпълнено или отхвърлено.

Ето пример за обещание от реалния свят:

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

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

Обещанията имат 3 състояния:

в очакване → обещанието на вашия приятел да ви купи блокче сладолед
разрешено → вашият приятел ви купува блокче сладолед
отхвърлено → вашият приятел не ви купува блокче сладолед

Нека превърнем нашия пример в обещание

new Promise( /* executor */ function(resolve, reject) { … } );

Стандартният начин за създаване на Promise е чрез използване на новия конструктор Promise. Това приема манипулатор, на който са дадени две функции като параметри. Първият манипулатор е функция, която се извиква с бъдещата стойност, когато е готова. Вторият манипулатор е функция за извикване, за да отхвърли обещанието, ако не може да разреши проблема.

var isFriendTruthful = false;
var willIGetIceCreamBar = new Promise( (resolve, reject) => {
 if (isFriendTruthful) {
   var iceCream = {
   brand: ‘Godiva’,
   flavor: ‘strawberry’
 };
 resolve(iceCream); // The Promise was fulfilled successfully 
 } else {
   var reason = new Error(friend is not truthful);
   reject(reason); // There was an error, the Promise was rejected
   }
 }
)

Няколко неща, които трябва да отбележите относно обещанията: те могат да бъдат изпълнени или отхвърлени само веднъж. Това е Обещанието, което се „урежда“. Можете също така незабавно да разрешите Promise, ако използвате метода Promise.resolve().

Време е да консумираме обещанието

Да консумираме обещанието означава, че искаме да обработим стойността на обещанието, след като то бъде изпълнено! За да се случи това, ние прикачваме нещо, наречено манипулатор, използвайки метода .then(). Този метод приема функция, на която ще бъде предадена разрешената стойност на обещанието. Разгледайте примера по-долу:

// We have a function askFriend that will consume our promise willIGetIceCreamBar
var askFriend = function () {
 willIGetIceCreamBar
// We use .then and .catch to handle our action
// What is the value of fulfilled? 
// It is the value you pass in your resolve()
 .then(function (fulfilled) {
   // yay, you got the ice cream!
   console.log(fulfilled);
   // output: { brand: Godiva, flavor: strawberry }
 })
// What is the value of error? It is the value you pass in your reject()
 .catch(function (error) {
   // oops, friend doesn’t buy it
   console.log(error.message);
   // output: friend is not truthful
 });
};
askFriend();

Методът .then() приема два параметъра. Първата извикана функция е, когато обещанието е изпълнено, докато втората функция се извиква, когато обещанието е отхвърлено. Ако използвате стандартния Promise.catch(), вие пропускате първия манипулатор, задавайки го като нула, и предоставяте само втория манипулатор, за да се справи с отхвърлянето. Използването на .catch() е по-ясно и ясно. Също така трябва само да добавите едно .catch() в края на верига (за което ще говорим по-нататък!), за да се справите с всяко отхвърляне или изхвърлени грешки в тази верига.

The following lead to the same outcome:
p.then((val) => console.log(“fulfilled:”, val)) 
 .catch((err) => console.log(“rejected:”, err));
p.then((val) => console.log(“fulfilled:”, val)) 
 .then(null, (err) => console.log(“rejected:”, err));

Свържете обещанията!

// call our promise
var askFriend = function () {
 willIGetIceCreamBar
 .then(shareIceCream) // chain it here
 .then(function (fulfilled) {
   console.log(fulfilled);
   // output: ‘Hey take a bite of this Godiva strawberry popsicle!’
 })
 .catch(function (error) {
   // oops, friend doesn't buy it
   console.log(error.message);
   // output: friend is not truthful
 });
};

Цялото това нещо работи, защото promise.then връща обещание, на което можем да извикаме друго .then. Когато манипулатор върне стойност, тя става резултат от това обещание, така че следващият .then се извиква с нея. Струва си да се отбележи, че ако върнатата стойност е обещание, всяко по-нататъшно изпълнение се спира, докато текущото обещание не бъде уредено.

За да отхвърлим автоматично обещание, можем да хвърлим изключение. Например:

var p1 = new Promise((resolve, reject) => { 
  if (true) {
    throw new Error(“rejected!”); // same as rejection
  } else {
    resolve(1); 
  }
});

Надявам се, че сте харесали краткото ми въведение в JavaScript Promises! Обещанията ще отнеме малко практика, преди да свикнете с тях, но след като започнете да навлизате в потока на съставянето на Promises, те са чудесен ресурс, който да имате в инструментариума си. Благодаря ви, че прочетохте!

Стеф