Как да използваме Promise обекти в JavaScript | Център за професионално обучение - Devise Expert

Как да използваме Promise обекти в JavaScript

Автор: Дикран Хачикян

Обектът Promise се използва при работа с асинхронни функции, например при изпращане на ajax заявки, и позволява на подобни функции да връщат резултати без да е необходимо да се използват функции с обратно извикване (callback funcitons), които водят до лошо структуриран код и редица други проблеми (например с обработката на грешки).

Към момента има множество библиотеки, които позволяват Promise обект да се включи към функциите в приложението, а някои библиотеки и софтуерни рамки също имат реализации на подобни обекти, но тази статия разглежда само  реализацията на Promise дадена в стандарта ES6.

Обектът Promise

По дефиниця Promise обекта представлява стойността или изхвърленото изключние като резултат  от изпълнението на асинхронна функция или функция, която не връща резултат с return.

Promise има три състояния:

  • изчакване  – начално състояние
  • изпълнено  – успешно изпълнена операция
  • отхвърлено – неуспено изпълнена операция,

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

Функцията asyncFunction() връща Promise обект като resolve() предизвиква изпълнение на метода then() на обекта, а reject() на метода catch() и съответно then() се използва за обработка на резултатите, например данни от асинхронната заявка, a catch() за обработка на грешките.

В следващият малък пример можете да видите как се използват resolve(), reject(), then() и catch():

Не е трудно да се види, че в този случай кодът изглежда доста по-подреден и ясен, отколкото с функции за обратно извикване, но с това не се изчерпват предимствата на Promise.  Функцията setTimeout() в примера е използвана за се опрости кода и да се получи изпълението асинхронно без да се налага да се правят ajax заявки.

Понеже функцията sendRequest() връща Promise обект, при зависими една от друга заявки, например след като се върнат данните за продукта трябва да се вземат коментарите на потребителите или подобните на него продукти, then() може да завтрши с return sendRequest() .

т.е. отделните извиквания на then() могат да се свържат верижно, което позволява да се избегне влагането на множество извиквания едно в друго.

Метод Promise.all()

В последният пример, в then() трябваше да се извика само веднъж sendRequest(), но в някои ситуации резултатът от първата заявка може да изисква многократно извикване на sendRequest() или друга асинхронна функция.

Например, ако първото извикване връща активните в момента потребители, то следващото извикване може да е към коментарите на всеки един потребител т.е. в then() се налага да се извика sendRequest() за всеки потребител, който е върнат в резултата от първата заявка.

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

Резултатът в then() e също масив от резултатите за всяка от функциите като редът на резултатите съответства на реда на обектите на във входния масива, а не на реда, в който са се върнали резултатите. Понеже all() връща всички резултати наведнъж, скоростта на изпълнение е равна на най-бавната от заявките.

Методът all() приключва с reject() при първия Promise обект, в който изпълнението е приключило с reject() независимо от състоянието на другите обекти.

Ако е необходимо просто да се изпълнят паралелно няколко независими една от друга функции, то с all() това ще изглежда така:

Метод Promise.race()

Методът race() е подобен на all(), но изпълнението на функциите, които връщат Promise обекти от масива, който се подава като параметър на race() приключва след първия resolve() (или reject() ).

В примера с sendRequest(), race() ще бъде подходящ ако имаме няколко алтернативни адреса, към които искаме да изпратим заявка, но да обработим резултата само от най-бързо изпълнилата се.

Кода на примерите от статията можете да свалите от профила ми в GitHub.