Future vs CompletableFuture in Java- #2

Reading Time: 3 minutes

In our previous blog – Future vs CompletableFuture – #1, we compared Java 5’s Future with Java 8’s CompletableFuture on the basis of two categories i.e. manual completion and attaching a callable method. Now, we will be comparing them on the basis of next 3 categories i.e.

  • Combining 2 CompletableFutures together
  • Combining multiple CompletableFutures together
  • Exception Handling

Let’s have a look at each one of them.

3. Combining 2 CompletableFutures together

In case of Future, there is no way to create asynchronous workflow i.e. long running computation. But CompletableFuture provides us with 2 methods to achieve this functionality:

i) thenCompose()

It is a method of combining 2 dependent futures together. This method takes a function that returns a CompletableFuture instance. The argument of this function is the result of the previous computation step. This allows us to use this value inside the next CompletableFuture‘s lambda.

For example:

Let’s compare this with the supplier method, thenApply() :

As you can see, the thenCompose() method is returning a value of type CompletableFuture whereas thenApply() is returning the value of type CompletableFuture in the same scenario.

Note: The thenCompose method together with thenApply implement basic building blocks of the monadic pattern. They closely relate to the map and flatMap methods of Stream and Optional classes also available in Java 8.

ii) thenCombine()

It is a method of combining 2 independent futures together and do something  with there result after both of them are complete. Combining is accomplished by taking 2 successful CompletionStages and having the results from both used as parameters to a BiFunction to produce another result.  For example:

iii) thenAcceptBoth()

It is used when you want to perform some operation with two independent Future’s result but don’t need to pass any resulting value down a Future chain. For example:

4. Combining multiple CompletableFutures together

What if there comes a scenario where you want to combine 100 different Futures that you want to run in parallel and then run some function after all of them completes. Future does not provide us any way in order to achieve this functionality but CompletableFuture does. There are two methods of implementing this:

i) CompletableFuture.allOf()

CompletableFuture.allOf()  static method is used in scenarios when you have a List of independent futures that you want to run in parallel and do something after all of them are complete.


The limitation of this method is that the return type is CompletableFuture i.e. it does not return the combined results of all Futures. Instead you have to manually get results from Futures.

Fortunately, CompletableFuture.join() method and Java 8 Streams API helps to resolve this issue:

Note : 

The CompletableFuture.join() method is similar to the CompletableFuture.get() method, but it throws an unchecked exception in case the Future does not complete normally. This makes it possible to use it as a method reference in the Stream.map() method.

ii) CompletableFuture.anyOf()

CompletableFuture.anyOf() as the name suggests, returns a new CompletableFuture which is completed when any of the given CompletableFutures complete, with the same result. CompletableFuture.anyOf() takes a varargs of Futures and returns CompletableFuture.

Limitation:The problem with CompletableFuture.anyOf() is that if you have CompletableFuture that return results of different types, then you won’t know the type of your final CompletableFuture.

5. Exception Handling

Let’s first understand how errors are propagated in a callback chain. Consider the following CompletableFuture callback chain –

If an error occurs in the original supplyAsync() task, then none of the thenApply() callbacks will be called and future will be resolved with the exception occurred. If an error occurs in first thenApply() callback then 2nd and 3rd callbacks won’t be called and the future will be resolved with the exception occurred, and so on.

So, there are 2 ways in order to handle this scenario:

i) Handle exceptions using exceptionally() callback

exceptionally() gives us a chance to recover by returning a default value or taking an alternative function that will be executed if preceding calculation fails with an exception.

ii) Handle exceptions using the generic handle() method

The API also provides a more generic method – handle() to recover from exceptions. It is called whether or not an exception occurs. If an exception occurs, then the result argument will be null, otherwise, the ex argument will be null.

Here is the link for the demo code and link for my previous blog.

References :


Written by 

Jasmine is a Software Consultant, with an experience of more than 11 months. She is familiar with Object Oriented Programming Paradigms and has a good command over C/C++. Apart from being a programmer, She is also fond of relational database technologies such as PostGreSQL & MySQL. Apart from that, work life balance is one of the most important thing for her. Her hobbies include watching netflix,chatting ,surfing net and writing.

1 thought on “Future vs CompletableFuture in Java- #24 min read

  1. Java 8 has some exciting features at the JVM level and language level. Absolute must to know features of Java 8 are :
    1. Lambda Expressions
    2. Parallel Operations
    3. Introduction of Nashorn
    4. New Date/Time API’s
    5. Concurrent Accumulators

Comments are closed.

%d bloggers like this: