Motivation for Completable Future
Remember the guy Future?
One which used to do tasks for us in a separate thread and return a future object where we can get the result.
Let’s refresh the memory with a small code snippet showing Future at work.
ExecutorService executorService = Executors.newFixedThreadPool(5); Callable customer = () -> { return findCustomerDetails(); }; Future<String> customerFuture = executorService.submit(customer); String customerStr = customerFuture.get();
Here, our code will wait until findCustomerDetails() works and return the result or the timeout happens. There is no way, to explicitly complete this future.
Let’s assume, we have to design a booking flow for parking. The flow looks like as follow:

Future does not provide any way for call back after the completion of getCustomerDetails and getParkingDetails, so that we can start booking operation just after.
We would ideally want to do these three operation chained together, and such chaining is not provided by Future APIs.
Similarly, Exception Handling has to be done outside the future APIs.
Completable Future At Work
We would like to approach completable future through examples.
private static void demoCompletableFuture() {
CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
System.out.println("Task Running inside completable Future");
});
}
Output :: Task Running inside completable Future
In the example above, the runAsync API of completable future takes runnable, which is provided in lambda. The example above does not show any difference between CompletableFuture as well as Future. But we will get to it.
With this introduction and motivation, it’s time to formally define Completable Future.
Definition
Completable Future implements Future. In a way, Completable Future is a future that can be explicitly completed. It can also act as an CompletionStage by providing way to support dependent functions. It also provides way to callback or in other words, support actions that can be triggered on it’s completion.
Salient Features
- When multiple threads wanted to complete, cancel or completeExceptionally , only one of them succeeds
- Actions supplied for dependent completions of non-async methods may be performed by the thread used by current completable future or any other caller of completion method
- Async methods without having an explicit Executor argument are performed using the ForkJoinPool.commonPool()
- In case of exceptional completion with a CompletionException, methods get() and get(long, TimeUnit) throw an ExecutionException
Usage
No Arguement
CompletableFuture<String> completableFuture = new CompletableFuture<>();
String value = completableFuture.get();
The above statement will cause program to run forever. Because there is nothing for the completable future to do.
RunAsync
private static void demoCompletableFuture() {
CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
System.out.println("Task Running inside completable Future");
});
}
Output :: Task Running inside completable Future
This has already been explained above. In the example above, the runAsync API of completable future takes runnable, which is provided in lambda.
SupplyAsync
Supply Async takes a supplier. Reminder: Supplier is a kind of functional interface that does not take any input but provides an output.
Here, we are giving Completable future a supplier lambda and returning a result which we then get as we would have done in Future.
The output of this program is as follow:
Task Running inside completable Future I am the returned result
ThenAccept
Let’s say you want to do some action after the completion of a Completable Future. There is nothing to be returned in that action, then you would use thenAccept as shown in the example above. ThenAccept takes input from the previous Completable Future and works on it.
The output of this program is as follow:
Task Running inside completable Future I am the returned result
ThenRun
Suppose you want to trigger some action after the completion of a completable future. And you don’t have any input from the completable feature as well as no output to be returned.
Output would be:
Task Running inside completable Future I don't get any input from the previous completableFuture. I am just triggered after it.
ThenCombine
ThenCombine allows to trigger a task after both the CompletableFuture is completed. Here, for example, we took 500ms to complete completableFutureFirst and after that, as soon as completableFutureSecond is completed, completableFutureThird is triggered.
Output is:
Task Running inside completable Future Task Running inside completable Future Time taken in the Completable Future operation is 563 Result is :: one plus two is three
Complete
For whatsoever reason, let’s say we have to complete the future, then we need to use completeAPI. Complete API will complete the future with whatever value we give in the arguement.
Output in this case would be:
Value After Complete: Completing the completable future with this default text
CompleteExceptionally
Assume something wrong happened with the task in CompletableFuture and you want to complete it and throw and exception so that the client of your API does appropriate handling of the exception. In such cases, you would use completeExceptionally.
Output for this is as follow:
Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.RuntimeException: Task in CompletableFuture Failed at java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:357) at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1895) at com.company.CompletableFutureAPIUsage.demoCompleteExceptionally(CompletableFutureAPIUsage.java:38) at com.company.CompletableFutureAPIUsage.main(CompletableFutureAPIUsage.java:20) Caused by: java.lang.RuntimeException: Task in CompletableFuture Failed at com.company.CompletableFutureAPIUsage.demoCompleteExceptionally(CompletableFutureAPIUsage.java:36) … 1 more
Cancel
You can cancel the future if you don’t need it anymore. Further, there is an option to cancel it if it’s running. CompletableFuture.get() throws an exception when you get in a cancelled completable future. isCancelled() API lets you check if it is cancelled or not.
Output is:
Task Running inside completable Future Completable Future is cancelled :: true Exception in thread "main" java.util.concurrent.CancellationException at java.util.concurrent.CompletableFuture.cancel(CompletableFuture.java:2263) at com.company.CompletableFutureAPIUsage.demoCancel(CompletableFutureAPIUsage.java:37) at com.company.CompletableFutureAPIUsage.main(CompletableFutureAPIUsage.java:22)
Github link for the programs shown above
https://github.com/rohitsingh20122992/completableFuture
Comparison of Iterative, Future as well as CompletableFuture
We have covered all the major concepts. We will try to cover remaining ones in later post.
Reference
If you liked this article and would like one such blog to land in your inbox every week, consider subscribing to our newsletter: https://skillcaptain.substack.com
Leave a Reply