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 — это аналог Dart-ового Future, но с более богатым API. Promise принимает экзекутор — функцию с параметрами resolve и reject. Статические методы Promise.all, Promise.race, Promise.allSettled позволяют комбинировать несколько промисов. Современные приложения используют 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.all переходит в rejected. Promise.race возвращает результат первого завершившегося промиса (успех или ошибка). Promise.allSettled ждёт все промисы независимо от ошибок — возвращает статус каждого.

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-style приводит к вложенности (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 для управления пулом потоков и поддерживает composition через 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 и snapshot состояния: 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. Fallback при ошибке реализуется через 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 — это предотвращает дублирование запросов. Для timeout используйте Future.timeout (Dart) или Promise.race с таймаутом, чтобы гарантировать, что асинхронная операция завершится в пределах заданного интервала.

Retry-паттерн в Future реализуется через рекурсивную цепочку: при ошибке в catch вызывается та же асинхронная функция с задержкой. Exponential backoff увеличивает интервал между попытками: 1 сек — 2 сек — 4 сек — до max 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.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 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

Читайте также