Promises
Obietnice (Promises) w JavaScript to kluczowy element asynchronicznego programowania. Są one jak umowy, które reprezentują wartość, która może być dostępna teraz, w przyszłości, lub nigdy - stąd nazwa “obietnica”.
Główną zaletą promisów jest ich zdolność do uproszczenia złożonych operacji asynchronicznych, takich jak pobieranie danych z sieci.
Jak działa Promise?
Kiedy tworzysz promise, dostarczasz funkcję, która zawiera “producenta” obietnicy. Ta funkcja ma dwa argumenty: resolve i reject.
resolvejest wywoływana, gdy asynchroniczna operacja zakończy się sukcesem.rejectjest używana, gdy operacja się nie powiedzie.
Promise może mieć jeden z trzech stanów:
- Oczekująca (
pending): początkowy stan, ani spełniona, ani odrzucona. - Spełniona (
fulfilled): oznacza, że operacja zakończyła się sukcesem. - Odrzucona (
rejected): oznacza, że operacja zakończyła się niepowodzeniem.
Przykład użycia
let promise = new Promise((resolve, reject) => {
// Kod asynchroniczny tutaj
if (/* operacja udana */) {
resolve("Sukces");
} else {
reject("Błąd");
}
}); Łańcuchowanie obietnic w JavaScript
Jedną z zalet obietnic jest ich zdolność do “łańcuchowania”. Możesz użyć .then() do obsługi spełnionych obietnic i .catch() do obsługi błędów.
promise
.then((value) => {
// Obsługa spełnionej obietnicy
})
.catch((error) => {
// Obsługa błędu
}); Wyobraźmy sobie, że pobierasz dane użytkownika z API:
fetchUserById(id)
.then((user) => {
console.log('Użytkownik:', user);
})
.catch((error) => {
console.error('Wystąpił błąd:', error);
}); Rozważmy teraz kolejny scenariusz: musimy pobrać dane użytkownika z API, a następnie, na podstawie tych danych, pobrać dodatkowe informacje z innego endpointu. Używając obietnic, możemy to zrobić w sposób uporządkowany i czytelny:
pobierzUzytkownika(123)
.then((uzytkownik) => pobierzDodatkoweInfo(uzytkownik.id))
.then((info) => {
console.log('Dodatkowe informacje:', info);
})
.catch((error) => {
console.error('Wystąpił błąd:', error);
}); W powyższym przykładzie, każde wywołanie .then() oczekuje na zakończenie poprzedniej operacji. Jeśli któraś z obietnic zostanie odrzucona, przechodzimy bezpośrednio do bloku .catch(), zarządzając błędami.
Obsługa błędów z obietnicami
Kluczowym aspektem pracy z obietnicami jest odpowiednie zarządzanie błędami. W przeciwieństwie do tradycyjnego podejścia z callbackami, obietnice oferują elegancki sposób na obsługę błędów za pomocą metody .catch().
Przechwytywanie błędów na różnych etapach łańcucha
Ważną cechą łańcuchowania obietnic jest to, że błąd w dowolnym miejscu łańcucha przerywa jego dalsze wykonywanie i przechodzi do najbliższego bloku .catch(). To pozwala na centralne zarządzanie błędami. Przyjrzyjmy się przykładowi:
pobierzUzytkownika(123)
.then((uzytkownik) => {
if (!uzytkownik) {
throw new Error('Użytkownik nie znaleziony');
}
return pobierzDodatkoweInfo(uzytkownik.id);
})
.then((info) => {
console.log('Dodatkowe informacje:', info);
})
.catch((error) => {
console.error('Wystąpił błąd:', error);
}); W tym przykładzie, jeśli użytkownik nie zostanie znaleziony, rzucony zostanie wyjątek, który natychmiast przeniesie nas do bloku .catch(), pomijając wszelkie kolejne etapy łańcucha.
Promise.all i Promise.race
👉 Promise.all
Promise.all jest niezwykle przydatną metodą w JavaScript, która pozwala na równoległe wykonywanie wielu promisów. Gdy pracujesz z asynchronicznymi operacjami, które są od siebie niezależne, Promise.all przychodzi z pomocą, umożliwiając efektywne zarządzanie tymi operacjami.
Promise.all([promise1, promise2, promise3])
.then((values) => {
// Działania po pomyślnym wykonaniu wszystkich obietnic
})
.catch((error) => {
// Działania w przypadku błędu w którejkolwiek z obietnic
}); W powyższym przykładzie, Promise.all przyjmuje tablicę obietnic i zwraca nową obietnicę. Nowa obietnica rozwiązuje się pomyślnie, gdy wszystkie obietnice w tablicy zostaną pomyślnie rozwiązane. Jeśli którakolwiek z obietnic zostanie odrzucona, cała Promise.all zostaje odrzucona.
Wyobraź sobie sytuację, w której musisz pobrać dane z różnych API. Każde zapytanie do API zwraca obietnicę. Używając Promise.all, możesz poczekać na wyniki wszystkich zapytań, zanim przejdziesz do następnego etapu przetwarzania danych.
👉 Promise.race
Promise.race to kolejna użyteczna metoda, która rozwiązuje się jak tylko pierwsza z przekazanych do niej obietnic zostanie rozwiązana lub odrzucona.
Promise.race([promise1, promise2, promise3])
.then((value) => {
// Działania po rozwiązaniu pierwszej obietnicy
})
.catch((error) => {
// Działania w przypadku błędu w pierwszej rozwiązanej obietnicy
}); W tym przypadku, Promise.race zwraca obietnicę, która rozwiązuje się lub odrzuca na podstawie pierwszej obietnicy, która zostanie rozwiązana lub odrzucona.
Jednym z przykładów zastosowania Promise.race może być stworzenie mechanizmu timeout dla asynchronicznych operacji. Możesz stworzyć obietnicę, która odrzuca się po określonym czasie i użyć Promise.race, aby wybrać, która operacja zakończy się szybciej: żądanie API czy timeout.