CompletableFuture
2 mins
CompletableFuture implements both the Future and CompletionStage interfaces, allowing you to build complex asynchronous pipelines with functional composition.
Source Code #
The CompletionStage Interface #
A CompletionStage represents a “stage” of a task. You can chain stages together to define what should happen after a task is finished, regardless of whether it succeeds or fails.
public interface CompletionStage<T> {
<U> CompletionStage<U> thenApply(Function<? super T,? extends U> fn);
CompletionStage<Void> thenAccept(Consumer<? super T> action);
CompletionStage<T> exceptionally(Function<Throwable, ? extends T> fn);
// ... 40+ methods for chaining ...
}
Canonical Usage #
When to use: Use CompletableFuture whenever you want to perform asynchronous work and chain its results into a pipeline (e.g., fetch data from a database, then call an external API, then log the result) without blocking the main thread.
Common Patterns:
- Async Supply: Using
supplyAsync(supplier, executor)to start an async task. - Functional Chaining: Using
thenApply,thenCompose, andthenCombineto build task graphs. - Error Handling: Using
exceptionallyorhandlefor robust async error recovery.
CompletableFuture.supplyAsync(() -> userService.getUser(id), executor)
.thenApply(user -> orderMapper.toOrder(user))
.thenCompose(order -> paymentService.processOrder(order))
.thenAccept(receipt -> logger.info("Payment complete: " + receipt))
.exceptionally(ex -> {
logger.error("Payment failed", ex);
return null;
});
Execution Strategies #
- Non-Async Methods:
thenApply(fn)usually runs in the same thread that completed the previous stage. - Async Methods:
thenApplyAsync(fn)runs the function in a separate thread (by default,ForkJoinPool.commonPool()).