Skip to main content

Future and Promises in Modern C++

A Complete Beginner's Guide to Asynchronous Programming

Updated 6 min readMarkdown
Part of seriesThe C++ Blueprint ๐Ÿš€
Future and Promises in Modern C++

Introduction

Imagine you're downloading a large file while editing a document. Your computer does not freeze waiting for the download to complete. It performs multiple tasks simultaneously.

As multi-core CPUs became common, C++ needed a standard mechanism for asynchronous programming and thread communication. C++11 introduced std::future and std::promise to solve this problem.


When Were Future and Promise Added?

Future and Promise were introduced in C++11.

#include <future>

The library introduced:

  • std::future

  • std::promise

  • std::async

  • std::packaged_task


Why Were Future and Promise Needed?

Before C++11, developers relied on:

  • Shared variables

  • Mutexes

  • Condition variables

  • Polling loops

  • Callback functions

Example:

int result;
bool ready = false;

Problems:

  • Race conditions

  • Deadlocks

  • Busy waiting

  • Difficult debugging

  • Complex synchronization logic

Future and Promise provide a clean abstraction that handles synchronization safely.


Real-Life Analogy

Food Delivery Example:

  • Restaurant prepares food โ†’ std::promise

  • Customer receives tracking token โ†’ std::future

The promise produces the result. The future receives the result later.


What is std::future?

A future represents a value that will become available later.

std::future<int>

Meaning:

I don't have the value yet, but I will receive it in the future.


What is std::promise?

A promise allows one thread to store a value which another thread retrieves later.

std::promise<int> promise;

Internal Working - Shared State

Future and Promise communicate through a hidden Shared State.

Producer Thread
      |
std::promise
      |
 Shared State
      |
std::future
      |
Consumer Thread

Promise writes data. Future reads data.


Future Lifecycle

Create Promise
      โ†“
Create Future
      โ†“
Future Waiting
      โ†“
Promise set_value()
      โ†“
Future Ready
      โ†“
future.get()
      โ†“
State Consumed

Basic Example

#include <future>
#include <thread>
#include <iostream>

void worker(std::promise<int> promise)
{
    std::this_thread::sleep_for(std::chrono::seconds(2));
    promise.set_value(42);
}

int main()
{
    std::promise<int> promise;
    auto future = promise.get_future();

    std::thread t(worker, std::move(promise));

    std::cout << "Waiting...
";

    int result = future.get();

    std::cout << result << std::endl;

    t.join();
}

Valid and Invalid Futures

After calling:

future.get();

The future becomes invalid.

if(future.valid())
{
    auto value = future.get();
}

Exception Propagation

One major advantage is safe exception transfer across threads.

try
{
    throw std::runtime_error("Database Error");
}
catch(...)
{
    promise.set_exception(std::current_exception());
}

Consumer:

try
{
    future.get();
}
catch(const std::exception& e)
{
    std::cout << e.what();
}

Waiting APIs

wait()

future.wait();

wait_for()

future.wait_for(std::chrono::seconds(2));

wait_until()

future.wait_until(deadline);

Using std::async

int calculate()
{
    return 10 * 20;
}

auto future = std::async(
                std::launch::async,
                calculate);

std::cout << future.get();

Output:

200

Async Launch Policies

std::launch::async

Creates a new thread.

std::async(std::launch::async, task);

std::launch::deferred

Executes only when get() is called.

std::async(std::launch::deferred, task);

Shared Future

Normal future supports one consumer.

std::future<int>

For multiple consumers:

std::shared_future<int>

Example:

auto sf = promise.get_future().share();

Multiple threads may call:

sf.get();

Packaged Task

std::packaged_task<int()> task(function);

Useful in:

  • Thread pools

  • Schedulers

  • Task queues


Future vs Promise vs Async

Promise

Produces value.

promise.set_value(100);

Future

Receives value.

future.get();

Async

Creates Promise and Future internally.

auto future = std::async(task);

Practical Industry Use Cases

Automotive ADAS

  • Camera processing

  • Radar processing

  • LiDAR processing

  • Sensor fusion

Example:

cameraFuture.get();
radarFuture.get();
lidarFuture.get();

performSensorFusion();

Cloud Services

  • OTA updates

  • Backend communication

  • Vehicle configuration downloads

Database Queries

Execute long-running queries asynchronously.

Image Processing

Pipeline stage synchronization.

GUI Applications

Keep UI responsive while background work continues.


Thread Pool Architecture

Task
 |
packaged_task
 |
Thread Pool
 |
future
 |
Consumer

Common in:

  • Trading systems

  • Backend servers

  • Game engines

  • ADAS middleware


Coroutines vs Future

Future and Promise:

C++11

Coroutines:

C++20

Future style:

auto value = future.get();

Coroutine style:

auto value = co_await task();

Common Mistakes

Calling get() Twice

future.get();
future.get();

Forgetting std::move

std::move(promise)

Never Calling set_value()

The waiting thread may block forever.


Performance Considerations

Good For:

  • One-time result transfer

  • Task synchronization

  • Exception propagation

  • Async operations

Not Good For:

  • High-frequency messaging

  • Real-time streaming

  • Massive message queues


Interview Questions

Difference between Future and Promise?

Promise produces data. Future consumes data.

Can Future be copied?

No, it is move-only.

Can one Promise create multiple Futures?

No.

Difference between Future and Shared Future?

Future supports one consumer. Shared Future supports multiple consumers.

What happens if Promise is destroyed before set_value()?

Future receives std::future_error.

Difference between get() and wait()?

  • get() waits and returns value

  • wait() only waits


Evolution of Modern C++

  • C++11 : Future, Promise, Async

  • C++14 : Async improvements

  • C++17 : Parallel algorithms

  • C++20 : Coroutines

  • C++23 : Concurrency improvements


Key Takeaways

Introduced in C++11

Found in <future> Simplifies thread synchronization

Eliminates busy polling

Supports exception propagation

Makes asynchronous programming easier

Widely used in automotive, embedded, backend and cloud systems Foundation for modern concurrency in C++


Conclusion

Future and Promise transformed C++ concurrency by providing a clean, safe, and standardized mechanism for asynchronous communication between threads. They hide much of the complexity of mutexes and condition variables while making code easier to write, debug, and maintain.

Whether you are building ADAS software, embedded systems, backend services, cloud platforms, or high-performance applications, understanding Future and Promise is essential for becoming a strong Modern C++ developer.


About the author

  • Satish Waghole Expert Developer | Automotive Software Architect | Tech Enthusiast

No comments yet. Be the first to comment.

More from this blog