Future/Promise — какво е това, асинхронни изчисления и програмиране

Автор: IT Sectr Публикувано: 2026-03-17 Време за четене: 9 мин

Future/Promise е модел на асинхронно програмиране, представляващ контейнер за резултата от все още незавършена операция. Според Dart Documentation, 2025, Future е read-only резултат от отложени изчисления, Promise е write-only договор, който попълва този резултат. Future се използва в Dart, Java, Scala и C++ за вериги then/catch.

Основни точки

  • Future — read-only контейнер за асинхронен резултат, достъпен в бъдеще
  • Promise — write-only попълвач, който приема резултата и го предава на Future
  • Вериги then — последователна обработка на асинхронни операции без вложени callback
  • Future в Dart — основа на async/await и библиотеките dio, http, shelf
  • Future/Promise — алтернатива на модела callback и основа за синтаксис async/await

Какво е Future/Promise?

Future/Promise е двойка взаимосвързани обекти, които разделят асинхронния резултат на две роли. Promise може да запише резултата (resolve) или грешката (reject). Future може само да го чете — чрез callback (then, catchError) или чрез await. Това разделение гарантира, че потребителят не може да промени резултата, а само да го използва.

Future в Dart

Езикът Dart използва Future като основен механизъм за асинхронност. Future<T> е договор за получаване на стойност от тип T в бъдеще. Future може да бъде в едно от три състояния: unresolved (операцията се изпълнява), resolved with value (успешно завършена) или resolved with error (завършена с грешка). След преминаване в състояние resolved, Future не се променя.

Promise в JavaScript

Promise в JavaScript е аналог на Future от Dart, но с по-богато API. Promise приема изпълнител — функция с параметри resolve и reject. Статичните методи Promise.all, Promise.race, Promise.allSettled позволяват комбиниране на няколко promise. Съвременните приложения използват синтаксис async/await върху Promise.

Как работи Future

Future работи на базата на event loop — цикъл на събития, който обработва асинхронни операции без блокиране на основната нишка. Когато се извика асинхронна функция (напр. http.get), тя незабавно връща Future. Реалната заявка се изпълнява на заден план, докато event loop продължава да обработва други събития. След завършване на операцията, Future преминава в състояние resolved и се извиква then.

Event loop е ключовият механизъм, върху който са изградени Dart и JavaScript. Event loop поддържа две опашки: microtask queue (кратки задачи, изпълнявани преди следващото събитие) и event queue (IO събития, таймери). Future.then поставя callback в microtask queue, Future.microtask — приоритетно изпълнение преди следващия кадър.

Веригите then() позволяват последователна обработка на резултатите от асинхронни операции без callback hell. Всеки then() получава резултата от предишната стъпка и връща нов Future. catchError() в края на веригата обработва всяка грешка, възникнала на която и да е стъпка. Това прави асинхронния код линеен и четим.

CompletableFuture в Java

В Java CompletableFuture разширява концепцията Future/Promise. Методът supplyAsync() стартира асинхронна задача в ForkJoinPool.commonPool(). thenApply() трансформира резултата, thenCompose() — разгъва вложен CompletableFuture. exceptionally() обработва грешката с връщане на стойност по подразбиране. allOf() и anyOf() работят като Promise.all и Promise.race в JavaScript. CompletableFuture поддържа ръчно завършване чрез complete() и completeExceptionally() — това е аналог на Promise resolve/reject.

Promise и договор за асинхронност

Promise е договор с едно от три състояния: pending (чакане), fulfilled (успешно завършен) или rejected (завършен с грешка). След преминаване в състояние fulfilled или rejected, Promise никога не се променя — това гарантира неизменяемост на резултата за всички потребители. Методът .then() се извиква асинхронно след преминаване във всяко завършено състояние.

Promise.all изчаква завършването на всички подадени promise и връща масив от резултати в същия ред. Ако един promise завърши с грешка — целият Promise.all преминава в rejected. Promise.race връща резултата от първия завършил promise (успех или грешка). Promise.allSettled изчаква всички promise независимо от грешките — връща статуса на всеки.

Chaining — ключовото предимство на Promise пред callback. Всеки .then() връща нов Promise, позволявайки изграждане на вериги с произволна дължина. Грешка във всяко звено на веригата се разпространява до най-близкия .catch(). Методът finally() изпълнява код след завършване на веригата независимо от резултата — за почистване на ресурси.

Примери за код Future (Dart)

Първи пример — създаване на Future чрез async-функция и обработка чрез then/catchError:

dart
Future<String> fetchData() async {
    await Future.delayed(Duration(seconds: 1))
    return "Данните са заредени"
}

fetchData()
    .then((result) => print(result))
    .catchError((error) => print("Грешка: $error"))

Втори пример — комбиниране на няколко Future чрез Future.wait:

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

Трети пример — Promise в JavaScript с ръчно създаване и верига then:

js
const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve("Готово")
    }, 1000)
})

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

Future vs други асинхронни модели

Future/Promise се различава от стила callback по това, че връща обект-договор, вместо да приема callback функция като аргумент. Стилът callback води до вложеност (callback hell), докато Future позволява изграждане на плоски вериги then(). Синтаксисът async/await прави кода с Future още по-линеен, подобен на синхронен код.

Future представлява точно един асинхронен резултат — една емисия, след която Future приключва. За разлика от Observable (Rx, ReactiveX) или Flow (Kotlin Coroutines), Future не поддържа множество стойности във времето. Ако имате нужда от поток от събития — използвайте Stream в Dart, Observable в Rx или Flow в Kotlin.

CompletableFuture в Java — аналог на Promise с разширено API: supplyAsync, thenApply, thenCompose, exceptionally. CompletableFuture позволява комбиниране на множество асинхронни операции чрез allOf, anyOf, както и задаване на собствен Executor за изпълнение. Scala Future използва ExecutionContext за управление на пула от нишки и поддържа композиция чрез for-comprehension.

В Dart моделът Future се различава от JavaScript Promise по това, че Dart има строго типизиране: Future<String> гарантира типа на връщаната стойност. Async функциите в Dart трябва да връщат Future и компилаторът проверява дали всички пътища връщат правилния тип. Future.wait е еквивалент на Promise.all — приема Iterable<Future<T>> и връща Future<List<T>>. Future.delayed и Future.error са удобни фабрики за тестване на асинхронни сценарии.

Future/Promise е имплементиран в Python чрез concurrent.futures.Future и asyncio.Future. concurrent.futures.ThreadPoolExecutor.submit() връща Future, на който може да се извика result() — блокиращо повикване. asyncio.Future е await-съвместим контейнер за асинхронен резултат в Asyncio. asyncio.gather() паралелно стартира няколко корутини и връща списък с резултати, подобно на Promise.all в JavaScript. Python също използва callback-based asyncio.ensure_future и вериги add_done_callback за композиция на асинхронни операции, но в модерен код се предпочита синтаксисът async/await с asyncio.Task.

В мобилната разработка Future се използва активно във Flutter приложения. Всяка мрежова заявка от пакета http или dio връща Future. FutureBuilder — уиджет, който приема Future и моментна снимка на състоянието: connectionDone, hasData, hasError. При получаване на данни, snapshot.data съдържа резултата от Future. StreamBuilder — аналог за множество стойности. FutureBuilder елиминира ръчното управление на състоянието на зареждане чрез setState.

Scala Future имплицитно използва ExecutionContext — пула от нишки, на който се изпълнява асинхронната задача. Композицията чрез map, flatMap, filter работи благодарение на това, че Future е монада. for-comprehension позволява последователно комбиниране на няколко Future без вложеност: for { a <- futureA; b <- futureB } yield a + b. Future.sequence работи като Promise.all. Връщането към резервен вариант при грешка се реализира чрез recover и recoverWith.

Future/Promise в C++ е представен от класовете std::future и std::promise от библиотеката <future>. std::async стартира асинхронна функция и връща std::future. std::promise позволява ръчно задаване на стойност чрез set_value() или грешка чрез set_exception(). За разлика от Dart и JavaScript, std::future.get() блокира нишката до получаване на резултата — това е важна разлика, която трябва да се вземе предвид при проектиране на многонишкови приложения в C++.

Модели за работа с Future

При проектиране на API, които връщат Future, следвайте правилото за единен източник на истина: всяка асинхронна заявка се създава веднъж и се кешира чрез Future.memoize. Ако два компонента поискат едни и същи данни, те получават един и същ Future — това предотвратява дублиране на заявки. За изчакване използвайте Future.timeout (Dart) или Promise.race с timeout, за да гарантирате, че асинхронната операция ще завърши в рамките на зададения интервал.

Модел Retry в Future се реализира чрез рекурсивна верига: при грешка в catch, същата асинхронна функция се извиква със закъснение. Exponential backoff увеличава интервала между опитите: 1 сек — 2 сек — 4 сек — до макс 30 сек. В Dart това се капсулира в extension на Future: future.retry(maxRetries: 3, delay: Duration(seconds: 1)). В Scala Future.zip обединява два независими резултата в кортеж — алтернатива на Promise.all за два Future. Отмяна (Cancellation) не се поддържа в стандартните Future, но се реализира чрез CancellationToken или AbortController в JavaScript.

Често задавани въпроси

Каква е разликата между Future и Promise?

Future е read-only контейнер за четене на асинхронен резултат чрез then или await. Promise е write-only договор, който приема стойност чрез resolve() и я свързва с Future. В Dart и JavaScript тези понятия са обединени: Future/Promise е един клас. В Java и Scala те са разделени.

С какво Future се различава от Observable?

Future представлява един асинхронен резултат — излъчва точно една стойност и приключва. Observable (Rx) излъчва множество стойности във времето — това е асинхронен поток. Ако имате нужда от един HTTP отговор — използвайте Future. Ако имате нужда от поток от промени в база данни — използвайте Observable или Stream.

Какво е async/await и как се свързва с Future?

async/await е синтактична захар над Future/Promise. Ключовата дума async маркира функция като асинхронна — тя връща Future. await спира изпълнението до завършване на Future, без да блокира нишката. В Dart, JavaScript, Python и C# async/await замени веригите then в повечето сценарии.

Как да обработваме грешки във верига Future?

В Dart използвайте .catchError() в края на веригата — той улавя изключения на всеки етап. В JavaScript — .catch(). В Dart е наличен и try/await/catch вътре в async функции. За възстановяване след грешка използвайте .catchError() с връщане на стойност по подразбиране или нов Future.

Какво е Promise.all и кога да го използваме?

Promise.all приема масив от promise и връща един Promise, който се разрешава с масив от резултати. Всички promise се стартират паралелно — това ускорява изпълнението на независими операции (напр. зареждане на множество файлове). Ако някой promise бъде отхвърлен — целият Promise.all се отхвърля със същата грешка.

Резюме

  • Future/Promise — модел на асинхронно програмиране с договор за една стойност
  • Future — read-only контейнер за четене на резултата от операция
  • Promise — write-only договор, който попълва Future чрез resolve/reject
  • Вериги then — плоска обработка на асинхронни стъпки без callback hell
  • async/await — синтактична захар над Future, прави кода линеен
  • Promise.all — паралелно стартиране на множество асинхронни операции
  • За множество стойности във времето използвайте Stream или Observable, а не Future

Ще разработим мобилно приложение под ключ

IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.

Обсъдете проекта

Прочетете също