AlgoMaster Logo

Future/Promise Pattern

18 min readUpdated December 16, 2025
Listen to this chapter
Unlock Audio

Future/Promise Pattern Explained

You're building a price comparison service. When a user searches for a product, you need to fetch prices from Amazon, eBay, and Walmart. Each API call takes 200-500ms.

The naive approach calls each API sequentially. Total time: 600-1500ms. Users wait while your server sits idle between network calls.

You could use threads and callbacks. Spawn three threads, each calls an API and invokes a callback with the result. But now you're managing thread lifecycles, synchronizing callbacks, handling partial failures, and your code looks like spaghetti.

The Future/Promise Pattern solves this elegantly. A Future is a placeholder for a result that will arrive later. You start all three API calls, get three Futures immediately, and combine them. The framework handles threading, synchronization, and composition. Your code stays clean and readable.

In this article, we'll explore:

  • What is the Future/Promise Pattern?
  • The problem it solves
  • How it works
  • Creating and consuming Futures
  • Composing multiple Futures
  • Error handling
  • Common pitfalls
  • When to use it in LLD interviews

If you're enjoying this newsletter and want to get even more value, consider becoming a [paid subscriber](https://blog.algomaster.io/subscribe).

As a paid subscriber, you'll unlock all premium articles and gain full access to all [premium courses](https://algomaster.io/newsletter/paid/resources) on [algomaster.io](https://algomaster.io).

Unlock Full Access

1. What is the Future/Promise Pattern?

The Future/Promise Pattern represents the result of an asynchronous computation. A Future is a read-only placeholder that will eventually contain a value or an error. A Promise is the writable side that completes the Future.

Think of it like ordering food at a restaurant. You get a receipt (Future) immediately. The kitchen (async task) prepares your food and delivers it (completes the Promise). You can do other things while waiting, and when the food is ready, you're notified.

Key Concepts

Term
Description

Future

Read-only handle to a pending result

Promise

Write-once handle to complete a Future

Pending

Result not yet available

Completed

Result available (success or failure)

Callback

Function called when Future completes

The Two Perspectives

2. The Problem: Blocking and Callback Hell

Without Futures, handling async operations is painful.

Problem 1: Blocking Wastes Resources

The thread blocks on each call, doing nothing while waiting for network responses.

Problem 2: Manual Threading is Complex

You manage thread creation, coordination, and error handling manually.

Problem 3: Callback Hell

Each async step adds a nesting level. Error handling duplicates everywhere.

The Solution: Futures

Start all calls, get Futures, combine them. The framework handles parallelism.

3. How Futures Work

A Future has a lifecycle from creation to completion.

Future Lifecycle

The Execution Flow

The caller gets the Future immediately and can:

  1. Block with get() until the result is ready
  2. Attach callbacks that execute when complete
  3. Compose with other Futures

4. Basic Implementation

4.1 Creating a Future

4.2 The Promise Side

A Promise lets you manually complete a Future.

4.3 Future States

5. Non-Blocking with Callbacks

Blocking defeats the purpose. Use callbacks to react when the Future completes.

Attaching Callbacks

Callback Flow

The main thread never blocks. When the result arrives, the callback runs.

6. Composing Futures

The real power of Futures is composition. Chain operations, combine results, handle errors declaratively.

6.1 Transform Results (map/thenApply)

Transform a Future's result without blocking.

6.2 Chain Async Operations (flatMap/thenCompose)

When one async operation depends on another's result.

No nesting. Each step flows into the next.

6.3 Combine Independent Futures

Run operations in parallel and combine results.

6.4 Race: First to Complete

Sometimes you only need the fastest response.

Composition Summary

Scroll
Operation
Use When
Example

thenApply

Transform result synchronously

Parse JSON to object

thenCompose

Chain async operations

Fetch user, then fetch orders

thenCombine

Combine two independent results

Merge data from two sources

allOf

Wait for all to complete

Fetch from multiple APIs

anyOf

Get first to complete

Query replicated servers

7. Error Handling

Futures propagate errors through the chain.

Error Propagation

An error at any step skips remaining steps and goes to the error handler.

Handling Errors

Recovery Strategies

8. Timeouts

Futures should not wait forever. Add timeouts to fail fast.

Adding Timeouts

9. Thread Pools and Executors

Futures need a thread pool to run tasks.

Execution Model

Choosing the Right Pool

Pool Type
Use Case

Fixed Thread Pool

CPU-bound tasks, known parallelism

Cached Thread Pool

Many short-lived tasks

Single Thread

Sequential async tasks

Fork/Join Pool

Recursive divide-and-conquer

Virtual Threads

High-volume I/O-bound tasks

10. Future vs Promise vs Callback

These terms often cause confusion.

Comparison

Scroll
Aspect
Callback
Future/Promise

Return value

None

Future immediately

Composition

Nested (callback hell)

Chained (flat)

Error handling

Per callback

Centralized

Cancellation

Difficult

Built-in

Multiple consumers

Tricky

Easy

Why Futures Win

11. Common Pitfalls

Pitfall 1: Blocking the Event Loop

Pitfall 2: Lost Exceptions

Pitfall 3: Thread Pool Exhaustion

Pitfall 4: Forgetting to Consume

Pitfall 5: Infinite Chains

Pitfall 6: No Timeout

12. Futures in Different Languages

Java

JavaScript

Python

C#

Go

13. When to Use in LLD Interviews

The Future/Promise Pattern appears frequently in system design.

Problem
How Futures Help

Design an API Gateway

Aggregate responses from multiple services

Design a Price Aggregator

Fetch prices in parallel, combine results

Design a Search System

Query multiple indexes concurrently

Design a Notification Service

Send to multiple channels without blocking

Design an Async Job Processor

Return job ID immediately, poll for result

Design a Cache with Async Loading

Return stale value, refresh in background

What to Mention in Interviews

  1. Problem Recognition: "These operations are independent and I/O-bound. I'll run them in parallel using Futures to reduce latency."
  2. Composition: "I'll use allOf to wait for all API calls, then combine the results."
  3. Error Handling: "Each Future can fail independently. I'll use exceptionally to provide fallback values so one failure doesn't break everything."
  4. Timeouts: "I'll set timeouts on each Future to fail fast if a service is slow."
  5. Thread Pool Choice: "For I/O-bound tasks, I'll use a cached thread pool. The common pool is for CPU-bound work only."
  6. Non-Blocking: "The calling thread returns immediately with a Future. No thread is blocked waiting for I/O."

14. Real-World Example: Price Aggregator

Let's implement the price comparison service from the introduction.

Key design decisions:

  • Each API call has a 2-second timeout
  • Failed calls return "unavailable" instead of breaking the whole request
  • All calls run in parallel on a dedicated executor

15. Summary

The Future/Promise Pattern represents asynchronous computation results as first-class values that can be composed, transformed, and handled declaratively.

Key Operations

Operation
Purpose

supplyAsync

Create Future from async task

thenApply

Transform result

thenCompose

Chain async operations

thenCombine

Combine two Futures

allOf

Wait for all

anyOf

Wait for first

exceptionally

Handle errors

orTimeout

Add timeout

Benefits

  • Non-blocking async operations
  • Composable transformations
  • Centralized error handling
  • Parallel execution made easy
  • Clean, readable code

When to Use

  • I/O-bound operations (network, disk)
  • Independent tasks that can run in parallel
  • Operations with natural async APIs
  • When you need cancellation or timeout

When NOT to Use

  • Simple synchronous operations
  • CPU-bound tasks on single thread
  • When blocking is acceptable and simpler
  • Real-time systems where latency matters

The Future/Promise Pattern is essential for building responsive, scalable systems. In LLD interviews, it demonstrates understanding of async programming, composition, and practical concurrency patterns.

References

Thank you for reading!

If you found it valuable, hit a like and consider subscribing for more such content every week.

If you have any questions or suggestions, leave a comment.

This post is public so feel free to share it.

Share

P.S. If you're enjoying this newsletter and want to get even more value, consider becoming a [paid subscriber](https://blog.algomaster.io/subscribe).

As a paid subscriber, you'll unlock all premium articles and gain full access to all [premium courses](https://algomaster.io/newsletter/paid/resources) on [algomaster.io](https://algomaster.io).

Unlock Full Access

There are [group discounts](https://blog.algomaster.io/subscribe?group=true), [gift options](https://blog.algomaster.io/subscribe?gift=true), and [referral bonuses](https://blog.algomaster.io/leaderboard) available.

Checkout my [Youtube channel](https://www.youtube.com/@ashishps_1/videos) for more in-depth content.

Follow me on [LinkedIn](https://www.linkedin.com/in/ashishps1/) and [X](https://twitter.com/ashishps_1) to stay updated.

Checkout my [GitHub repositories](https://github.com/ashishps1) for free interview preparation resources.

I hope you have a lovely day!

See you soon, Ashish