Explain Codes LogoExplain Codes Logo

What's the difference between a Future and a Promise?

java
async-programming
future
promise
Nikita BarsukovbyNikita Barsukov·Oct 22, 2024
TLDR

A Future is essentially a container, holding the result of a computation that might yet be available. It provides simple means (isDone(), get()) to check completion and fetch the outcome. On the other hand, a Promise, gives you the wheels to manually set the results of the Future, being its manufacturer, usually from another thread.

Example in Java that illustrates how CompletableFuture can act as both Future and Promise:

CompletableFuture<String> future = new CompletableFuture<>(); // Promise: The Future whisperer future.complete("Eureka"); // Future: The fortune teller String result = future.get(); // "Eureka"

The complete() method is where the Promise magic happens, while get() is the Future's way to say "patience is a virtue" and fetches the result.

Digging deeper: CompletableFuture

CompletableFuture is a Future and a Promise in one. But it doesn't stop there — it stands out with a flamboyant API catering to an array of use-cases. It's the James Bond of the Future world. With methods like thenApply, thenCompose, and thenAccept, it facilitates asynchronous executions while maintaining an impressive savoir-faire and readability.

CompletableFuture.supplyAsync(() -> "Initial stage") .thenApply(stuff -> stuff + ", then this happened") .thenAccept(System.out::println);

Here, we've initiated an asynchronous operation that later transforms the result and consumes the output all without blocking the current thread — like a ninja.

Promises in action

Promise with CompletableFuture is not a mere standalone object. It’s more like firing a starting gun for an asynchronous task you control. But be warned — promises are meant to be kept. A CompletableFuture can be fulfilled once, and only once. Double fulfillment will make CompletableFuture grumpy, resulting in an IllegalStateException.

Synchronization concerns

When working with promises, be aware of potential race conditions. Promises can be fulfilled by any thread. Synchronizing this process or using thread-safe methods like complete(), completeExceptionally(), and cancel() is akin to having a disciplined orchestra where no musician is overstepping their part.

Patterns and practices for async programming

  • Promise composition: Combine multiple promises using allOf, anyOf methods, control the flow of operation and handle results as they appear.
  • Common workflows: Abstract frequently used asynchronous flows into methods that return CompletableFuture. Drastically improves code maintainability and reduces copy-paste mistrals.
  • Timeout safeguards: Always include a timeout mechanism to abort blocking tasks. It's like the emergency stop button, only cooler.

Advanced usage & tips

  • Leverage descriptive methods: Utilize methods like thenCombine, thenCompose, allOf to effectively manage dependencies between tasks.
  • Graceful error-handling: Adopt error handling strategy using handle, exceptionally, and whenComplete. Remember, even Batman has a contingency plan.
  • Thread execution: Learn when to supply an Executor to control the thread of operation and avoid the default ForkJoinPool when necessary. You're the conductor of this symphony.

In all the blissful chaos of promises and futures, remember to handle potential race conditions and always encapsulate operations that could lead to concurrency concerns.