Obietnice - Promise
Promise
Obietnice (Promise) pozwalają nam nieco inaczej podejść do pracy z asynchronicznymi operacjami.
Dzięki nim możemy wykonać jakiś kod, a następnie odpowiednio zareagować na jego wykonanie. Można powiedzieć, że to taka inna odmiana funkcji callback.
Kiedy kupisz mi burgera, wtedy będę zadowolony
Praca z obietnicami w zasadzie zawsze składa się z 2 kroków. Po pierwsze za pomocą konstruktora Promise tworzymy obietnicę. Po drugie za pomocą odpowiedniej funkcji które nam udostępnia reagujemy na jej zakończenie. Zupełnie jak w powyższym zdaniu.

Tworzenie Promise
Do stworzenia Promise korzystamy z konstruktora Promise(), który w parametrze przyjmuje funkcję, do której przekazujemy referencję do dwóch funkcji (tak zwanych egzekutorów), które będą wywoływane w przypadku zwrócenia poprawnego lub błędnego kodu.
Każda obietnica może zakończyć się na dwa sposoby - powodzeniem (resolve) i niepowodzeniem (reject). Gdy obietnica zakończy się powodzeniem (np. dane się wczytają), powinniśmy wywołać funkcję resolve(), przekazując do niej rezultat działania. W przypadku błędów powinniśmy wywołać funkcję reject(), do której przekażemy błędne dane lub komunikat błędu.
Po stworzeniu nowego obiektu Promise, w pierwszym momencie jest on w stanie oczekiwania (w debuggerze widoczna jest właściwość [[PromiseStatus]] ustawiona na "pending" oraz właściwość [[PromiseValue]] ustawiona na undefined.

Gdzieś w tle dzieją się asynchroniczne operacje, które zajmują jakiś czas (np. trwa ściąganie danych).
W momencie zakończenia wykonywania takich operacji Promise przechodzi w stan "settled" (ustalony/załatwiony) i zostaje zwrócony jakiś wynik. Status takiego promise przełączany jest odpowiednio w "fulfilled" lub "rejected", a my jako programiści wywołujemy przekazane w parametrach funkcje (resolve lub reject) z przekazanymi do nich wynikami operacji.

Konsumpcja Promise
Po zakończeniu Promise możemy zareagować na jej wynik. Służą do tego dodatkowe metody, które klasa Promise nam udostępnia. Pierwszą z nich jest metoda then(). Pozwala ona reagować zarówno na pozytywne rozwiązanie obietnicy, negatywne jak i oba na raz:
catch()
Obietnica może zakończyć się pozytywnie (resolve) lub negatywnie (reject). Do reakcji na negatywną odpowiedź możemy użyć albo drugiego parametru funkcji then(), albo funkcji catch() (stosowane częściej).
Promise.all()
Bardzo często nasze czynności chcielibyśmy zacząć wykonywać dopiero po zakończeniu wszystkich asynchronicznych operacji. Przykładem takiej sytuacji może być widok użytkownika, na którym wyświetlamy jego dane, jego galerię, książki, posiadane zwierzęta (koniecznie!...). Aby poczekać na zakończenie wszystkich takich operacji użyjemy funkcji Promise.all() do której przekażemy tablicę zawierającą nasze obietnice:
Promise.allSettled()
Podobną w działaniu jest funkcja allSettled().
Różnica w porównaniu z all() jest taka, że funkcja all() zwraca wynik, gdy wszystkie przekazane do niej obietnice zostaną zakończone pozytywnie, natomiast allSettled() zwraca wynik gdy się zakończą - nie ważne czy pozytywnie czy negatywnie.
Promise.race()
Jeżeli powyższe obie metody czekały na zakończenie wszystkich obietnic, tak metoda Promise.race() zwróci pierwszą zakończoną obietnicę:
W powyższym przypadku zostały zwrócone dane "pets data". Wynika to tylko z faktu, że jako pierwsza wykonała się obietnica z funkcji loadPets(). Gdyby pierwsza zakończyła się obietnica z loadBooks() (reject), kod przeszedł by do funkcji catch().
Promise.finally()
Funkcja finally() pozwala nam zareagować na moment, kiedy wszystkie operacje się zakończą - bez względu czy zakończyły się pozytywnie czy negatywnie. Wszystkie osoby, które używały $.ajax() w jQuery z pewnością pamiętają zdarzenie complete() - to właśnie jego odpowiednik.
Łańcuchowe obietnice
Jeżeli dana funkcja zwróci nam nową obietnicę, możemy na niej wykonać jedną z powyższych funkcji czyli then(), catch() itp.
Każda z takich funkcji także zwraca obietnicę, więc możemy wykonać kolejne operacje za pomocą kolejnych then():
Takie łańcuchowe wywoływanie kolejnych obietnic jest o tyle istotne, ponieważ bardzo często przy tworzeniu funkcji używających obietnic, niektóre operacje będziemy wykonywać wewnątrz jakiś funkcji, natomiast resztę poza jej ciałem reagując na zwróconą obietnicę:
Przyda nam się to szczególnie, gdy będziemy pisać kod pobierające dane z serwera. Część obróbki danych wykonamy wewnątrz funkcji, natomiast cała reszta trafi poza funkcję.
O funkcji fetch będziemy uczyć się w jednym z kolejnych rozdziałów. Dla nas najważniejsze teraz jest to, że sama w sobie zwraca obietnicę, dzięki czemu mogę zareagować na jej zakończenie - dokładnie tak jak w poprzednich przykładach.
Zauważ przy okazji, że do zwrócenia błędu wykorzystałem funkcję statyczną Promise.reject(), którą udostępnia nam klasa Promise (drugą jest Promise.resolve()).
W powyższej sytuacji mogę też rzucić po prostu błędem:
Last updated