Pre

Wprowadzenie do Promise.allSettled — co to takiego i dlaczego ma sens

W świecie asynchroniczności w JavaScript pojawia się wiele narzędzi, które pomagają programistom kontrolować przepływ operacji. Jednym z nich jest Promise.allSettled, statyczna metoda obiektu Promise, która pozwala na jednoczesne “rozstrzygnięcie” wielu obietnic (promises) i zwrócenie szczegółowej informacji o ich statusach. W praktyce oznacza to, że niezależnie od tego, czy poszczególne obietnice zakończą się sukcesem, czy błędem, Promise.allSettled zwróci tablicę wyników, w której każdy element opisuje stan konkretnej obietnicy. To potężne narzędzie do pracy z równoległymi zapytaniami do sieci, operacjami na plikach czy zadaniami obciążającymi CPU, gdzie zależy nam na pełnym zestawie rezultatów bez przerywania całego procesu po pierwszym errorze.

Promise.allSettled a Promise.all: najważniejsze różnice

Kluczową decyzją projektową w JavaScript jest to, czy chcemy, aby cała grupa obietnic zakończyła się sukcesem tylko wtedy, gdy wszystkie jej elementy zakończą się pozytywnie. W tym celu używa się Promise.all. Jednak zachowanie Promise.all jest bardzo restrykcyjne: jeśli choć jedna obietnica odrzuci (reject), całość zostanie odrzucona. W scenariuszach, gdzie błędy poszczególnych zadań są nieistotne lub chcemy mieć pełny raport wszystkich wyników, warto skorzystać z Promise.allSettled.

Promise.allSettled zwraca w pełni informacyjną listę, gdzie każdy element odpowiada jednej obietnicy i zawiera status oraz odpowiednie dane: status: "fulfilled" z wartością lub status: "rejected" z powodem odrzucenia. Dzięki temu łatwo analizujemy, które zadania się powiodły, a które nie, bez przerywania całej operacji po napotkaniu błędu.

Krótka lista najważniejszych różnic

  • Promise.all: jeśli którakolwiek obietnica odrzuca, cała operacja kończy się odrzuceniem (reject).
  • Promise.allSettled: zwraca tablicę wyników dla wszystkich obietnic, nawet jeśli niektóre zakończyły się błędem.
  • Struktura zwracanych danych: Promise.all -> array of fulfilled values (jeśli wszystkie się powiodą) lub odrzucenie; Promise.allSettled -> array z obiektami zawierającymi status, value lub reason.

W praktyce oznacza to koniec z frustracją spowodowaną przerywaniem całej pracy z powodu pojedynczego errora. Z Promise.allSettled mamy pełny obraz sytuacji i możemy podjąć decyzje na podstawie wszystkich danych.

Jak działa Promise.allSettled: mechanika działania krok po kroku

Gdy uruchamiamy Promise.allSettled(i) z tablicą obietnic lub wartości, ten mechanizm wykonuje je równolegle, czekając aż wszystkie zostaną rozstrzygnięte („settled”). Rozstrzygnięcie oznacza either fulfilled (z wartością) albo rejected (z powodem). Następnie zwracana jest pojedyncza obietnica, która resolve’uje się z tablicą wyników. Każdy wynik ma postać obiektu z pól status oraz odpowiednimi danymi:

{
  status: "fulfilled",
  value: ... // w przypadku powodzenia
}
{
  status: "rejected",
  reason: ... // w przypadku odrzucenia
}

Najczęściej zadawane pytania dotyczące Promise.allSettled

Czy Promise.allSettled zwraca wyniki w tej samej kolejności co wejście?

Tak. Wyniki zwracane przez Promise.allSettled są ułożone w tej samej kolejności co wejście, niezależnie od tego, w jakiej kolejności faktycznie rozstrzygały się poszczególne obietnice. To znacząca zaleta, bo łatwo łączymy poszczególne odpowiedzi z konkretnymi zadaniami bez konieczności dopasowywania ich po czasie.

Jakie dane zwraca Promise.allSettled dla wartości obietnic?

Dla obietnic, które zakończyły się powodzeniem, zwracany obiekt ma status fulfilled i pole value z wartością. Dla obietnic odrzuconych jest to rejected i pole reason z powodem odrzucenia. Dzięki temu łatwo odtworzyć, które zadania się powiodły, a które wymagały ponownego podejścia lub dodatkowej obsługi błędu.

Czy Promise.allSettled obsługuje również wartości niebędące obietnicami?

Tak. Jeśli wejściem jest tablica wartości (niekoniecznie obietnic), Promise.allSettled przekształca każdą wartość na obietnicę, a następnie wykonuje podobnie jak w przypadku obietnic. To wygodny wzorzec, gdy mamy mieszankę gotowych danych i asynchronicznych operacji.

Przykłady zastosowań w praktyce

Poniżej prezentuję kilka scenariuszy, w których Promise.allSettled sprawdza się doskonale. Każdy przykład ilustruje inny kontekst użycia i pokazuje, jak utrzymać pełne raporty wyników bez przerywania całej operacji z powodu pojedynczego błędu.

Scenariusz 1: równoległe pobieranie danych z wielu źródeł

Wyobraź sobie aplikację, która musi pobrać dane z kilku zewnętrznych API. Każde wywołanie może zakończyć się sukcesem lub błędem z powodu awarii sieci lub błędu serwera. Dzięki Promise.allSettled możemy pobrać wszystkie dane i na koniec zebrać pełny raport:

// Przykładowy kod: pobieranie z wielu endpointów
const endpoints = [
  '/api/user/1',
  '/api/user/2',
  '/api/user/3',
  '/api/user/4'
];

const requests = endpoints.map(url => fetch(url).then(res => res.ok ? res.json() : Promise.reject(new Error('Błąd: ' + url))));

// Promise.allSettled zwróci wynik dla każdego żądania
Promise.allSettled(requests)
  .then(results => {
    results.forEach((r, idx) => {
      if (r.status === 'fulfilled') {
        console.log('Dane z', endpoints[idx], ':', r.value);
      } else {
        console.warn('Żądanie do', endpoints[idx], 'zakończyło się błędem:', r.reason);
      }
    });
  });

Scenariusz 2: operacje na wielu plikach

Przy przetwarzaniu wielu plików w środowisku Node.js, gdzie operacje IO mogą zakończyć się sukcesem lub błędem, Promise.allSettled pozwala na zapisanie wyników wszystkich operacji i późniejszą analizę:

// Przykładowy kod: odczyt wielu plików
const fs = require('fs').promises;
const files = ['a.txt', 'b.txt', 'c.txt', 'd.txt'];

Promise.allSettled(files.map(f => fs.readFile(f, 'utf8')))
  .then(results => {
    results.forEach((r, i) => {
      if (r.status === 'fulfilled') {
        console.log(files[i], 'zawartość:', r.value);
      } else {
        console.error(files[i], 'nie udało się odczytać. Powód:', r.reason);
      }
    });
  });

Scenariusz 3: równoległe walidacje danych i agregacja wyników

Gdy mamy zestaw operacji walidacyjnych, które mogą zakończyć się błędem, a chcemy uzyskać pełny raport, Promise.allSettled okazuje się bardzo przydatny:

// Przykładowy kod: walidacja danych
const validators = [
  data => data.age > 0 ? Promise.resolve(data) : Promise.reject('Wiek musi być dodatni'),
  data => data.email.includes('@') ? Promise.resolve(data) : Promise.reject('Zły adres email'),
  data => data.name.length > 1 ? Promise.resolve(data) : Promise.reject('Nazwa musi mieć co najmniej 2 znaki')
].map(v => v({ name: 'Jan', age: 25, email: 'jan@example.com' }));

Promise.allSettled(validators)
  .then(results => {
    const passed = results.filter(r => r.status === 'fulfilled').map(r => r.value);
    const failed = results.filter(r => r.status === 'rejected').map(r => r.reason);
    console.log('Poprawne dane:', passed.length);
    console.log('Błędy walidacji:', failed);
  });

Najczęstsze pułapki i dobre praktyki pracy z Promise.allSettled

Jak poradzić sobie z dużymi tablicami obietnic

Gdy mamy bardzo dużą liczbę zadań, rozważ podział na porcje (chunking) i łączenie wyników po każdej partii. Nie zawsze musimy uruchamiać wszystkie obietnice naraz – złożone scenariusze mogą wymagać ograniczenia współbieżności, aby uniknąć przeciążenia sieci lub systemu plików. W takich przypadkach Promise.allSettled wciąż będzie idealny do zapewnienia pełnego raportu.

Obsługa błędów i retry logic

Chociaż Promise.allSettled nie przerywa po odrzuceniu, warto mieć plan na powtórki (retry) dla błędnych operacji. Możemy na przykład zebrać listę powodów błędów i w kolejnym kroku spróbować ponownie tylko te zadania, które zakończyły się błędem. W ten sposób redukujemy liczbę błędów, a jednocześnie zachowujemy pełny raport o pierwotnym stanie wszystkich obietnic.

Różnice między środowiskami – przeglądarki i Node.js

Promise.allSettled jest dostępny w nowoczesnych środowiskach JavaScript. W przeglądarkach zostało wprowadzone w ramach ES2020, a w Node.js obsługa zaczęła się pojawiać w nowszych wersjach po migracji na implementacje zgodne z ES2020. W praktyce oznacza to, że w projektach skierowanych do starszych środowisk warto użyć polyfillu lub biblioteki, która emuluje to zachowanie, dopóki wszystkie środowiska nie zostaną zaktualizowane. Pamiętaj jednak, że polyfille nie zawsze są idealną zamienką — testuj kod w docelowych przeglądarkach i wersjach Node.

Wytrzymałość kodu i czytelność z Promise.allSettled

Jak utrzymać czysty kod when using Promise.allSettled

Chociaż Promise.allSettled daje potężne możliwości, warto dbać o czytelność. Rozdzielanie logiki na funkcje pomocnicze, które mapują wyniki do ustandaryzowanej struktury, pomaga łatwo łączyć wyniki z interfejsem użytkownika, logować problemy oraz monitorować wydajność. Dzięki temu kod staje się bardziej przewidywalny i łatwiejszy w utrzymaniu, co jest kluczowe w dużych projektach.

Podstawowe wzorce projektowe z Promise.allSettled

Najczęściej spotykane wzorce obejmują:

  • Raportowanie stanu wszystkich operacji w UI, bez blokowania użytkownika.
  • Agregowanie danych z wielu źródeł i wyświetlanie ich razem, nawet jeśli niektóre źródła zawiodły.
  • Implementacja mechanizmów retry dla błędów wybranych zadań z zachowaniem pełnych rezultatów.

Wsparcie środowiskowe i kompatybilność

Promise.allSettled jest częścią standardu ES2020 i jest wspierane przez większość nowoczesnych przeglądarek oraz środowisk wykonawczych JavaScript, takich jak Node.js w wersjach po 12.9.0. Aby upewnić się o zgodności z konkretną wersją, warto zajrzeć do dokumentacji MDN lub dokumentacji konkretnego środowiska. Dzięki temu six-step testy zawsze będą działać tak, jak oczekujemy, bez niespodzianek związanych z brakiem wsparcia lub różnicą w implementacji.

Najlepsze praktyki podczas pracy z Promise.allSettled

Dokładne monitorowanie i logowanie

Podczas pracy z dużymi zestawami asynchronicznych operacji warto logować zarówno sukcesy, jak i błędy. Dzięki temu łatwiejsze staje się diagnozowanie problemów, a także optymalizacja zapytań i sposobu ich wykonywania. Użycie Promise.allSettled w połączeniu z odpowiednimi wskaźnikami wydajności (np. performance.now) pozwala zebrać metryki czasów realizacji i odrzucenia w jednym miejscu.

Bezpieczeństwo typów i walidacja wejścia

Przy operacjach, które zwracają różne typy danych, zadbaj o jednolity interfejs zwracany przez status/value lub status/reason. Dzięki temu łatwiej odczytujesz dane z wyników i unikasz błędów związanych z nieoczekiwanym typem wartości. To szczególnie ważne w TypeScript, gdzie definicje interfejsów mogą znacząco poprawić bezpieczeństwo kodu.

Optymalizacja i ograniczanie współbieżności

Choć Promise.allSettled działa bardzo efektywnie, nadmierna liczba równoczesnych operacji może prowadzić do przeciążenia serwera lub sieci. Dlatego w praktyce warto implementować mechanizmy ograniczające jednoczesność (concurrency control), szczególnie w środowiskach produkcyjnych. Połączenie takiego podejścia z Promise.allSettled daje zarówno stabilność, jak i pełny zestaw wyników do raportowania.

Wersje literackie i nazewnictwo w kontekście SEO i pisania technicznego

Pod kątem SEO i czytelności ważne jest, aby w tekście pojawiały się różne formy zapisu, które odzwierciedlają naturalne sposoby wyszukiwania. Najważniejszym i formalnie poprawnym zapisem jest Promise.allSettled (capital P i S, zgodnie ze standardem JavaScript). W tekście można także wspomnieć o alternatywnym, rzadziej spotykanym zapisie promise.allsettled lub odwołać się do samego terminu „allSettled” w kontekście koncepcyjnym, jednak w praktycznych przykładach i w dokumentacji technicznej powinno być używane wyłącznie formalne Promise.allSettled. Dzięki temu unikamy nieporozumień i zapewniamy jasność przekazu dla programistów poszukujących rozwiązania w sieci.

Podsumowanie: dlaczego warto używać Promise.allSettled

Promise.allSettled to potężne narzędzie w arsenale dewelopera JavaScript, które umożliwia bezpieczne operacje równoległe z pełnym raportem wyników. Dzięki temu łatwo tworzyć stabilne interfejsy użytkownika, które potrafią sumować dane z wielu źródeł, jednocześnie obsługując błędy w sposób spójny i kontrolowany. Z perspektywy praktycznej, Promise.allSettled eliminuje typowy problem Promise.all, gdzie jedna odrzucona obietnica niszczy całą operację. Wprowadza nowy poziom elastyczności: estymacja, logika retry, agregacja danych i transparentność stanu poszczególnych zadań.

Najważniejsze punkty do zapamiętania

  • Promise.allSettled zwraca tablicę wyników o stałej długości, odpowiadającej wejściu.
  • Każdy wynik to obiekt z pól status, oraz odpowiednio value albo reason.
  • Umożliwia pełny raport bez przerywania w przypadku błędów w poszczególnych zadaniach.
  • Sprawdza się idealnie w scenariuszach pobierania danych, walidacji danych, operacjach IO i podobnych równoległych zadaniach.

Końcowa refleksja: praktyczny przewodnik po implementacji

Jeżeli dopiero zaczynasz pracę z Promise.allSettled, zacznij od prostych scenariuszy, w których masz kilka asynchronicznych operacji (np. fetch z różnych źródeł). Stopniowo rozszerzaj swoje użycie, dodając logikę obsługi błędów, retry, ograniczanie współbieżności i integrację z interfejsem użytkownika. Dzięki temu zbudujesz solidny, odporny na błędy kod, który jednocześnie dostarcza użytkownikowi pełny obraz sytuacji. W miarę jak Twoje projekty będą rosły, Promise.allSettled stanie się naturalnym wyborem do zarządzania równoległymi operacjami i integralną częścią architektury asynchronicznej w JavaScripcie.