Future/Promise — co to jest, obliczenia asynchroniczne i programowanie

Autor: IT Sectr Opublikowano: 2026-03-17 Czas czytania: 9 min

Future/Promise to wzorzec programowania asynchronicznego, reprezentujący kontener na wynik jeszcze niezakończonej operacji. Według Dart Documentation, 2025, Future to read-only wynik odroczonych obliczeń, Promise to write-only kontrakt, który ten wynik wypełnia. Future jest używany w Dart, Java, Scala i C++ dla łańcuchów then/catch.

Najważniejsze

  • Future — read-only kontener na wynik asynchroniczny dostępny w przyszłości
  • Promise — write-only wypełniacz, który przyjmuje wynik i przekazuje go do Future
  • Łańcuchy then — sekwencyjne przetwarzanie operacji asynchronicznych bez zagnieżdżonych callback
  • Future w Dart — podstawa async/await i bibliotek dio, http, shelf
  • Future/Promise — alternatywa dla wzorca callback i podstawa składni async/await

Co to jest Future/Promise?

Future/Promise to para powiązanych obiektów, które dzielą wynik asynchroniczny na dwie role. Promise może zapisać wynik (resolve) lub błąd (reject). Future może go tylko odczytać — przez callback (then, catchError) lub przez await. Taki podział gwarantuje, że konsument nie może zmienić wyniku, a jedynie go użyć.

Future w Dart

Język Dart używa Future jako głównego mechanizmu asynchroniczności. Future<T> to kontrakt na otrzymanie wartości typu T w przyszłości. Future może być w jednym z trzech stanów: unresolved (operacja jest wykonywana), resolved with value (zakończona pomyślnie) lub resolved with error (zakończona błędem). Po przejściu w stan resolved Future nie zmienia się.

Promise w JavaScript

Promise w JavaScript to odpowiednik Future z Darta, ale z bogatszym API. Promise przyjmuje executor — funkcję z parametrami resolve i reject. Statyczne metody Promise.all, Promise.race, Promise.allSettled pozwalają łączyć wiele promiseów. Nowoczesne aplikacje używają składni async/await opartej na Promise.

Jak działa Future

Future działa w oparciu o event loop — pętlę zdarzeń, która przetwarza operacje asynchroniczne bez blokowania głównego wątku. Kiedy wywoływana jest funkcja asynchroniczna (np. http.get), natychmiast zwraca Future. Rzeczywiste żądanie jest wykonywane w tle, a event loop kontynuuje przetwarzanie innych zdarzeń. Po zakończeniu operacji Future przechodzi w stan resolved i wywoływany jest then.

Event loop to kluczowy mechanizm, na którym zbudowane są Dart i JavaScript. Event loop utrzymuje dwie kolejki: microtask queue (krótkie zadania wykonywane przed następnym zdarzeniem) i event queue (zdarzenia IO, timery). Future.then umieszcza callback w microtask queue, Future.microtask — priorytetowe wykonanie przed następną klatką.

Łańcuchy then() pozwalają na sekwencyjne przetwarzanie wyników operacji asynchronicznych bez callback hell. Każdy then() otrzymuje wynik poprzedniego kroku i zwraca nowy Future. catchError() na końcu łańcucha obsługuje dowolny błąd, który wystąpił na którymkolwiek etapie. To sprawia, że kod asynchroniczny staje się liniowy i czytelny.

CompletableFuture w Java

W Java CompletableFuture rozszerza koncepcję Future/Promise. Metoda supplyAsync() uruchamia zadanie asynchroniczne w ForkJoinPool.commonPool(). thenApply() transformuje wynik, thenCompose() — rozwija zagnieżdżony CompletableFuture. exceptionally() obsługuje błąd, zwracając wartość domyślną. allOf() i anyOf() działają jak Promise.all i Promise.race w JavaScript. CompletableFuture obsługuje ręczne zakończenie przez complete() i completeExceptionally() — to odpowiednik Promise resolve/reject.

Promise i kontrakt asynchroniczności

Promise to kontrakt z jednym z trzech stanów: pending (oczekiwanie), fulfilled (zakończony pomyślnie) lub rejected (zakończony błędem). Po przejściu w stan fulfilled lub rejected Promise nigdy się nie zmienia — gwarantuje to niezmienność wyniku dla wszystkich konsumentów. Metoda .then() jest wywoływana asynchronicznie po przejściu w dowolny stan zakończony.

Promise.all oczekuje na zakończenie wszystkich przekazanych promiseów i zwraca tablicę wyników w tej samej kolejności. Jeśli jeden promise zakończył się błędem — cały Promise.all przechodzi w stan rejected. Promise.race zwraca wynik pierwszego zakończonego promisea (sukces lub błąd). Promise.allSettled czeka na wszystkie promisey niezależnie od błędów — zwraca status każdego.

Chaining — kluczowa zaleta Promise w porównaniu z callback. Każdy .then() zwraca nowy Promise, pozwalając na budowanie łańcuchów o dowolnej długości. Błąd w dowolnym ogniwie łańcucha jest propagowany do najbliższego .catch(). Metoda finally() wykonuje kod po zakończeniu łańcucha niezależnie od wyniku — do czyszczenia zasobów.

Przykłady kodu Future (Dart)

Pierwszy przykład — tworzenie Future przez async-funkcję i obsługa przez then/catchError:

dart
Future<String> fetchData() async {
    await Future.delayed(Duration(seconds: 1))
    return "Dane załadowane"
}

fetchData()
    .then((result) => print(result))
    .catchError((error) => print("Błąd: $error"))

Drugi przykład — łączenie wielu Future przez Future.wait:

dart
Future<List<String>> loadAll() async {
    final futures = [
        fetchFromApi("/users"),
        fetchFromApi("/posts"),
        fetchFromApi("/comments")
    ]
    final results = await Future.wait(futures)
    return results
}

Trzeci przykład — Promise w JavaScript z ręcznym tworzeniem i łańcuchem then:

js
const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve("Gotowe")
    }, 1000)
})

promise
    .then(value => console.log(value))
    .catch(err => console.error(err))

Future a inne wzorce asynchroniczne

Future/Promise różni się od stylu callback tym, że zwraca obiekt-kontrakt zamiast przyjmować funkcję zwrotną jako argument. Styl callback prowadzi do zagnieżdżenia (callback hell), podczas gdy Future pozwala na budowanie płaskich łańcuchów then(). Składnia async/await sprawia, że kod z Future jest jeszcze bardziej liniowy, przypominający synchroniczny.

Future reprezentuje dokładnie jeden wynik asynchroniczny — jedna emisja, po której Future się kończy. W przeciwieństwie do Observable (Rx, ReactiveX) lub Flow (Kotlin Coroutines), Future nie obsługuje wielokrotnych wartości w czasie. Jeśli potrzebny jest strumień zdarzeń — użyj Stream w Dart, Observable w Rx lub Flow w Kotlin.

CompletableFuture w Java — odpowiednik Promise z rozszerzonym API: supplyAsync, thenApply, thenCompose, exceptionally. CompletableFuture pozwala łączyć wiele operacji asynchronicznych przez allOf, anyOf, a także ustawić własny Executor do wykonania. Scala Future używa ExecutionContext do zarządzania pulą wątków i obsługuje kompozycję przez for-comprehension.

W Dart wzorzec Future różni się od JavaScript Promise tym, że Dart ma ścisłe typowanie: Future<String> gwarantuje typ zwracanej wartości. Funkcje async w Dart muszą zwracać Future, a kompilator sprawdza, czy wszystkie ścieżki zwracają poprawny typ. Future.wait jest odpowiednikiem Promise.all — przyjmuje Iterable<Future<T>> i zwraca Future<List<T>>. Future.delayed i Future.error to wygodne fabryki do testowania scenariuszy asynchronicznych.

Future/Promise jest zaimplementowany w Python przez concurrent.futures.Future i asyncio.Future. concurrent.futures.ThreadPoolExecutor.submit() zwraca Future, na którym można wywołać result() — wywołanie blokujące. asyncio.Future to await-kompatybilny kontener na wynik asynchroniczny w Asyncio. asyncio.gather() równolegle uruchamia kilka korutyn i zwraca listę wyników, podobnie jak Promise.all w JavaScript. Python używa również callback-based asyncio.ensure_future i łańcuchów add_done_callback do kompozycji operacji asynchronicznych, ale w nowoczesnym kodzie preferowana jest składnia async/await z asyncio.Task.

W programowaniu mobilnym Future jest aktywnie używany w aplikacjach Flutter. Każde żądanie sieciowe z pakietu http lub dio zwraca Future. FutureBuilder — widget, który przyjmuje Future i snapshot stanu: connectionDone, hasData, hasError. Po otrzymaniu danych snapshot.data zawiera wynik Future. StreamBuilder — odpowiednik dla wielokrotnych wartości. FutureBuilder eliminuje ręczne zarządzanie stanem ładowania przez setState.

Scala Future używa ExecutionContext niejawnie — puli wątków, na której wykonywane jest zadanie asynchroniczne. Kompozycja przez map, flatMap, filter działa dzięki temu, że Future to monada. for-comprehension pozwala na sekwencyjne łączenie wielu Future bez zagnieżdżenia: for { a <- futureA; b <- futureB } yield a + b. Future.sequence działa jak Promise.all. Obsługa błędu przy fallback realizowana jest przez recover i recoverWith.

Future/Promise w C++ jest reprezentowany przez klasę std::future i std::promise z biblioteki <future>. std::async uruchamia funkcję asynchroniczną i zwraca std::future. std::promise pozwala ręcznie ustawić wartość przez set_value() lub błąd przez set_exception(). W przeciwieństwie do Dart i JavaScript, std::future.get() blokuje wątek do momentu otrzymania wyniku — to ważna różnica, którą należy uwzględnić przy projektowaniu aplikacji wielowątkowych w C++.

Wzorce pracy z Future

Przy projektowaniu API zwracających Future, przestrzegaj zasady jedynego źródła prawdy: każde żądanie asynchroniczne jest tworzone raz i cacheowane przez Future.memoize. Jeśli dwa komponenty żądają tych samych danych, otrzymują ten sam Future — zapobiega to duplikowaniu żądań. Dla timeoutu użyj Future.timeout (Dart) lub Promise.race z timeoutem, aby zagwarantować, że operacja asynchroniczna zakończy się w określonym przedziale czasu.

Wzorzec Retry w Future jest realizowany przez rekurencyjny łańcuch: przy błędzie w catch wywoływana jest ta sama funkcja asynchroniczna z opóźnieniem. Exponential backoff zwiększa odstęp między próbami: 1 sek — 2 sek — 4 sek — do max 30 sek. W Dart jest to hermetyzowane w extension na Future: future.retry(maxRetries: 3, delay: Duration(seconds: 1)). W Scala Future.zip łączy dwa niezależne wyniki w krotkę — alternatywa dla Promise.all dla dwóch Future. Cancellation w standardowych Future nie jest obsługiwana, ale jest realizowana przez CancellationToken lub AbortController w JavaScript.

Często zadawane pytania

Jaka jest różnica między Future a Promise?

Future to read-only kontener do odczytu wyniku asynchronicznego przez then lub await. Promise to write-only kontrakt, który przyjmuje wartość przez resolve() i wiąże ją z Future. W Dart i JavaScript te pojęcia są połączone: Future/Promise to jedna klasa. W Java i Scala są rozdzielone.

Czym Future różni się od Observable?

Future reprezentuje pojedynczy wynik asynchroniczny — emituje dokładnie jedną wartość i kończy się. Observable (Rx) emituje wielokrotne wartości w czasie — to strumień asynchroniczny. Jeśli potrzebujesz jednej odpowiedzi HTTP — użyj Future. Jeśli potrzebujesz strumienia zmian bazy danych — użyj Observable lub Stream.

Czym jest async/await i jak to się wiąże z Future?

async/await to lukier składniowy nad Future/Promise. Słowo kluczowe async oznacza funkcję jako asynchroniczną — zwraca ona Future. await wstrzymuje wykonanie do czasu zakończenia Future, nie blokując wątku. W Dart, JavaScript, Python i C# async/await zastąpił łańcuchy then w większości scenariuszy.

Jak obsługiwać błędy w łańcuchu Future?

W Dart użyj .catchError() na końcu łańcucha — przechwytuje on wyjątki na każdym etapie. W JavaScript — .catch(). W Dart dostępny jest również try/await/catch wewnątrz funkcji async. Do odzyskiwania po błędzie użyj .catchError() z zwróceniem wartości domyślnej lub nowego Future.

Czym jest Promise.all i kiedy go używać?

Promise.all przyjmuje tablicę promiseów i zwraca jeden Promise, który rozwiązuje się tablicą wyników. Wszystkie promisy uruchamiane są równolegle — przyspiesza to wykonywanie niezależnych operacji (np. ładowanie wielu plików). Jeśli którykolwiek promise zostanie odrzucony — cały Promise.all jest odrzucany z tym samym błędem.

Podsumowanie

  • Future/Promise — wzorzec programowania asynchronicznego z kontraktem na jedną wartość
  • Future — read-only kontener do odczytu wyniku operacji
  • Promise — write-only kontrakt wypełniający Future przez resolve/reject
  • Łańcuchy then — płaskie przetwarzanie kroków asynchronicznych bez callback hell
  • async/await — lukier składniowy nad Future, czyniący kod liniowym
  • Promise.all — równoległe uruchamianie wielu operacji asynchronicznych
  • Dla wielokrotnych wartości w czasie używaj Stream lub Observable, a nie Future

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również