Future/Promise — What It Is, Async Computations and Programming

Author: IT Sectr Published: 2026-03-17 Reading time: 9 min

Future/Promise is an asynchronous programming pattern that represents a container for the result of an operation that hasn’t completed yet. According to Dart Documentation, 2025, Future is a read-only result of deferred computations, Promise is a write-only contract that fills this result. Future is used in Dart, Java, Scala and C++ for then/catch chains.

Key Takeaways

  • Future — a read-only container for an asynchronous result available in the future
  • Promise — a write-only placeholder that accepts the result and passes it to the Future
  • then chains — sequential processing of asynchronous operations without nested callbacks
  • Future in Dart — the foundation of async/await and dio, http, shelf libraries
  • Future/Promise — an alternative to the callback pattern and the basis for async/await syntax

What is Future/Promise?

Future/Promise is a pair of interconnected objects that split an asynchronous result into two roles. Promise can write the result (resolve) or an error (reject). Future can only read it — through callbacks (then, catchError) or through await. This separation guarantees that the consumer cannot change the result, only use it.

Future in Dart

The Dart language uses Future as its main asynchronous mechanism. Future<T> is a contract to receive a value of type T in the future. A Future can be in one of three states: unresolved (operation in progress), resolved with value (successfully completed), or resolved with error (completed with an error). Once it transitions to a resolved state, the Future never changes.

Promise in JavaScript

Promise in JavaScript is analogous to Dart’s Future, but with a richer API. Promise takes an executor — a function with resolve and reject parameters. Static methods Promise.all, Promise.race, Promise.allSettled allow combining multiple promises. Modern applications use async/await syntax on top of Promise.

How Future Works

Future works based on an event loop — a cycle of events that processes asynchronous operations without blocking the main thread. When an async function is called (e.g., http.get), it immediately returns a Future. The actual request runs in the background, while the event loop continues processing other events. After the operation completes, the Future transitions to a resolved state and then is called.

Event loop is the key mechanism on which Dart and JavaScript are built. The event loop maintains two queues: the microtask queue (short tasks, executed before the next event) and the event queue (IO events, timers). Future.then places a callback in the microtask queue, Future.microtask — prioritized execution before the next frame.

Chains of then() allow sequential processing of asynchronous operation results without callback hell. Each then() receives the result of the previous step and returns a new Future. catchError() at the end of the chain handles any error that occurred at any step. This makes asynchronous code linear and readable.

CompletableFuture in Java

In Java, CompletableFuture extends the Future/Promise concept. The supplyAsync() method launches an asynchronous task in ForkJoinPool.commonPool(). thenApply() transforms the result, thenCompose() unwraps a nested CompletableFuture. exceptionally() handles an error by returning a default value. allOf() and anyOf() work like Promise.all and Promise.race in JavaScript. CompletableFuture supports manual completion via complete() and completeExceptionally() — this is analogous to Promise resolve/reject.

Promise and the Async Contract

Promise is a contract with one of three states: pending, fulfilled, or rejected. Once it transitions to fulfilled or rejected, the Promise never changes — this guarantees result immutability for all consumers. The .then() method is called asynchronously after transitioning to any completed state.

Promise.all waits for all passed promises to complete and returns an array of results in the same order. If one promise fails with an error — the entire Promise.all transitions to rejected. Promise.race returns the result of the first completed promise (success or error). Promise.allSettled waits for all promises regardless of errors — returns the status of each.

Chaining is the key advantage of Promise over callbacks. Each .then() returns a new Promise, allowing chains of arbitrary length. An error in any link of the chain propagates to the nearest .catch(). The finally() method executes code after the chain completes regardless of the result — for resource cleanup.

Future Code Examples (Dart)

The first example — creating a Future via an async function and handling it with then/catchError:

dart
Future<String> fetchData() async {
    await Future.delayed(Duration(seconds: 1))
    return "Data loaded"
}

fetchData()
    .then((result) => print(result))
    .catchError((error) => print("Error: $error"))

The second example — combining multiple Futures with Future.wait:

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

The third example — Promise in JavaScript with manual creation and a then chain:

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

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

Future vs Other Async Patterns

Future/Promise differs from the callback style by returning a contract object instead of accepting a callback function as an argument. Callback-style leads to nesting (callback hell), while Future allows building flat then() chains. async/await syntax makes Future code even more linear, resembling synchronous code.

Future represents exactly one asynchronous result — one emission, after which the Future completes. Unlike Observable (Rx, ReactiveX) or Flow (Kotlin Coroutines), Future does not support multiple values over time. If you need a stream of events — use Stream in Dart, Observable in Rx, or Flow in Kotlin.

CompletableFuture in Java is an analog of Promise with an extended API: supplyAsync, thenApply, thenCompose, exceptionally. CompletableFuture allows combining multiple asynchronous operations via allOf, anyOf, and also allows specifying a custom Executor for execution. Scala Future uses ExecutionContext to manage the thread pool and supports composition via for-comprehension.

In Dart, the Future pattern differs from JavaScript Promise in that Dart has strict typing: Future<String> guarantees the return value type. async functions in Dart must return a Future, and the compiler checks that all paths return a correct type. Future.wait is equivalent to Promise.all — it takes Iterable<Future<T>> and returns Future<List<T>>. Future.delayed and Future.error are convenient factories for testing async scenarios.

Future/Promise is implemented in Python via concurrent.futures.Future and asyncio.Future. concurrent.futures.ThreadPoolExecutor.submit() returns a Future, on which you can call result() — a blocking call. asyncio.Future is an await-compatible container for an async result in Asyncio. asyncio.gather() runs multiple coroutines in parallel and returns a list of results, similar to Promise.all in JavaScript. Python also uses callback-based asyncio.ensure_future and add_done_callback chains for composing async operations, but in modern code, async/await syntax with asyncio.Task is preferred.

In mobile development, Future is actively used in Flutter applications. Every network request from the http or dio package returns a Future. FutureBuilder is a widget that takes a Future and a snapshot state: connectionDone, hasData, hasError. When data is received, snapshot.data contains the Future result. StreamBuilder is the analog for multiple values. FutureBuilder eliminates manual loading state management via setState.

Scala Future uses ExecutionContext implicitly — the thread pool on which the async task executes. Composition via map, flatMap, filter works because Future is a monad. for-comprehension allows sequentially combining multiple Futures without nesting: for { a <- futureA; b <- futureB } yield a + b. Future.sequence works like Promise.all. Error fallback is implemented via recover and recoverWith.

Future/Promise in C++ is represented by the std::future and std::promise classes from the <future> library. std::async launches an async function and returns std::future. std::promise allows manually setting a value via set_value() or an error via set_exception(). Unlike Dart and JavaScript, std::future.get() blocks the thread until the result is received — this is an important difference to consider when designing multithreaded C++ applications.

Future Patterns

When designing APIs that return Futures, follow the single source of truth rule: each async request is created once and cached via Future.memoize. If two components request the same data, they receive the same Future — this prevents duplicate requests. For timeout, use Future.timeout (Dart) or Promise.race with a timeout to guarantee that the async operation completes within a given interval.

The Retry pattern in Future is implemented via a recursive chain: on error in catch, the same async function is called with a delay. Exponential backoff increases the interval between attempts: 1 sec — 2 sec — 4 sec — up to max 30 sec. In Dart this is encapsulated in an extension on Future: future.retry(maxRetries: 3, delay: Duration(seconds: 1)). In Scala, Future.zip combines two independent results into a tuple — an alternative to Promise.all for two Futures. Cancellation is not supported in standard Futures, but is implemented via CancellationToken or AbortController in JavaScript.

Frequently Asked Questions

What is the difference between Future and Promise?

Future is a read-only container for reading an async result via then or await. Promise is a write-only contract that accepts a value via resolve() and links it to the Future. In Dart and JavaScript, these concepts are combined: Future/Promise is one class. In Java and Scala they are separated.

How is Future different from Observable?

Future represents a single async result — it emits exactly one value and completes. Observable (Rx) emits multiple values over time — it is an async stream. If you need one HTTP response — use Future. If you need a stream of database changes — use Observable or Stream.

What is async/await and how is it related to Future?

async/await is syntactic sugar over Future/Promise. The async keyword marks a function as asynchronous — it returns a Future. await pauses execution until the Future completes, without blocking the thread. In Dart, JavaScript, Python, and C#, async/await has replaced then chains in most scenarios.

How to handle errors in a Future chain?

In Dart, use .catchError() at the end of the chain — it catches exceptions at any stage. In JavaScript — .catch(). Dart also has try/await/catch inside async functions. For error recovery, use .catchError() with a default value return or a new Future.

What is Promise.all and when to use it?

Promise.all takes an array of promises and returns a single Promise that resolves with an array of results. All promises run in parallel — this speeds up independent operations (e.g., loading multiple files). If any promise is rejected — the entire Promise.all is rejected with the same error.

Summary

  • Future/Promise — an async programming pattern with a contract for a single value
  • Future — a read-only container for reading an operation result
  • Promise — a write-only contract that fills the Future via resolve/reject
  • then chains — flat processing of async steps without callback hell
  • async/await — syntactic sugar over Future, making code linear
  • Promise.all — parallel execution of multiple async operations
  • For multiple values over time, use Stream or Observable, not Future

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also