Obietnice powstały z myślą o ulepszeniu pracy z kodem asynchronicznym, z jakim mamy do czynienia w JavaScripcie. Ich podstawowe zalety to lepsza kontrola w zakresie synchronizacji wywołań, obsługa błędów oraz lepsza czytelność. Obiekt Promise został wprowadzony w ECMAScripcie 2015 (wcześniej ES6) w czerwcu 2015 roku Specyfikację można znaleźć tutaj, natomiast wsparcie prezentuje się następująco:
Jak widać, jeśli chcemy wspierać Internet Explorer, musimy posiłkować się polyfillem (można znaleźć go tutaj lub tutaj).
O czym w ogóle mowa? Promise jest obiektem, który jest używany jako symbol zastępczy dla ostatecznych wyników odroczonych i najczęściej asynchronicznych obliczeń. Przy utworzeniu, obietnica jest w stanie oczekiwania, a jej rezultat to undefined. Stan obietnicy p może być:
- wypełniony, jeśli p.then(f, r) kolejkuje zadanie do wywołania f,
- odrzucony, jeśli p.then(f, r) kolejkuje zadanie do wywołania r,
- oczekujący, jeśli nie jest wypełniony ani odrzucony,
- ustalony, jeśli nie jest oczekujący,
- rozstrzygnięty, jeśli obietnica p została ustalona lub jest zablokowana, aby dopasować swój stan do stanu innej obietnicy.
A po co nam obietnice? Spójrzmy na poniższy przykład, w którym mamy funkcje zapisujące notatki i sztucznie wywołane opóźnienie 2000ms, które mogłoby mieć miejsce np. podczas przesyłania na serwer. Aby zawsze otrzymać aktualną listę notek, użyliśmy odwoływania się do callbacków, co jak widać szybko tworzy nam brzydkie schodki:
Jak tego uniknąć? Tu właśnie na pomoc przychodzą nam obietnice, które mogą uprościć powyższy kod do takiej oto postaci:
Przejdźmy w takim razie do rozgryzienia obietnic krok po kroku. Poniżej widzimy podstawową deklarację i użycie obiektu Promise:
Jak widać, cała rzecz przedstawia się dosyć intuicyjnie, więc bez większych wstępów przejdźmy do ćwiczeń – postaraj się odgadnąć, jaki będzie wynik, a rezultaty będą widoczne po najechaniu kursorem na rozmycie pod obrazem.
// 1
// 1
Jak dotąd wszystko jasne prawda? Więc jak myślisz, co dostaniemy poniżej:
// 1, dlatego, że promise oczekuje tylko jednego rezultatu, drugi dotrze już po jego rozwiązaniu
Skoro znasz już odpowiedź na powyższe zadania, jak uważasz, co zostanie wylogowane w takich sytuacjach:
// 1, dlatego, że Promise może być albo wypełniony, albo błędny, nigdy oba na raz – tutaj pierwszy był resolve
// 2, gdyż tym razem pierwszy był reject
Oprócz wyżej pokazanych metod na rozstrzyganie obietnic, mamy jeszcze kolejne, bardzo pomocne w sytuacjach, gdy używanych promise’ów jest kilka:
Teraz, gdy już o obietnicach wiemy więcej, czas pójść o krok (lub parę) dalej :) Przyjrzyjmy się poniższym przykładom:
// 1 1 1, gdyż ustawiliśmy 3 listenery na sukces
// 1 undefined undefined, gdyż kolejne funkcje nic nie zwracają!
// 1 2 3
// 1 2 4
A teraz, mając w pamięci powyższe przykłady, ten poniżej powinien wylogować… no właśnie, co? Udało się? ;)
// 1 catch wywołany zostanie tylko przez pierwszego rejecta – aby wejść do kolejnych funkcji musielibyśmy mieć .then() jak wyżej lub zwrócić błąd
Tak więc po raz kolejny zbierzmy wszystkie zdobyte informacje do tej pory, i pochylmy się nad poniższym kodem:
// 1 2 3
Użyjmy teraz do przykładu obiektu Error:
// 1 2 Error: 3
A co w przypadku zwrócenia Promise.resolve()?
// 10 20
Jeszcze jeden przykład z Erorrem:
// 1 Error: 2 4
Jak myślisz, jaką kolejność otrzymamy tutaj?
// 1 2 3
Mam nadzieję, że czujesz się już w miarę pewnie, gdyż zaraz przyspieszamy! Weźmy pod lupę takie przykłady:
// 1 3 2 4
// 1 3 4 2
A teraz wróćmy do użycia setTimeout(). Co otrzymamy poniżej? Hmm…
// automation marketing SALESmanago
Skoro tak, to analogicznie poniższy przykład powinien dać…
// manago SALES
I na tym zakończymy dzisiejsze ćwiczenia! Postaraj się teraz wykorzystać w praktyce zdobytą wiedzę i dziel się nią z innymi, powodzenia!