
1. Overview
In this article, we will discuss the behavior of the CompletableFuture allOf method. The CompletableFuture interface was added to the java.util.concurrent
package in the Java 8 release.
The purpose of CompletableFuture
interface is asynchronous programming. In other words, it runs the code as a task in a non-blocking thread. After execution, it notifies the caller thread about the task progress, completion, or any failure. To learn more about CompletableFuture, refer to our articles.
2. CompletableFuture allOf method
The allOf
method of the CompletableFuture
accepts an array of CompletableFutures
as an argument and returns a new CompletableFuture
. Using the new CompletableFuture
, you can wait for those provided CompletableFutures
to complete normally or exceptionally.
You had to wait for this returned
to complete by using any of the callback methods such as CompletableFuture
get()
, join()
, whenComplete()
so on.
If any of the provided CompletableFutures
complete exceptionally, then the returned CompletableFuture
also throws a CompletionException
having the aforesaid exception as its cause. Note that the returned CompletableFuture
does not reflect the results of the provided CompletableFutures
. However, you can get the result by inspecting each CompletableFuture
individually.
Assume our project registers a new user after validating their details. So we have two CompletableFuture
instances: one for validating the details and another for registering the new user. If the validation and registration are successful, then we want to update UI success or failure if any exceptions.
So we had to create a new CompletableFuture
using allOf
method which can wait for these two CompletableFuture
instances to complete and return an exception if any.
Let’s see a few examples to understand the allOf
method.
2.1. allOf with no exceptions
The below CompletableFuture
validates the new user.
// first CompletableFuture CompletableFuture<Boolean> validateNewUser = CompletableFuture.supplyAsync(() -> { try { // validate new user details // inducing sleep for demo Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Validation of new user : SUCCESS"); return true; });
The below CompletableFuture
handles the registration of the new user.
// second CompletableFuture CompletableFuture<Boolean> registerNewUser = CompletableFuture.supplyAsync(() -> { try { // perform registration // inducing sleep for demo purpose Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Registration of new user : success"); return true; });
In the below code, the allOf
method waits for both validateNewUser
and registerNewUser
to complete and returns a new CompletableFuture
finalCompletableFuture
. Note that you have to wait for the returned finalCompletableFuture
to complete. Here, we are using the whenComplete
to wait asynchronously.
Once all the provided CompletableFuture
completes, then the whenComplete
method of the finalCompletableFuture
executes. You can check throwable
for any exception.
CompletableFuture<Void> finalCompletableFuture = CompletableFuture.allOf(validateNewUser, registerNewUser); finalCompletableFuture.whenComplete((result, throwable) -> { System.out.println("Result : " + result); System.out.println("Throwable : " + throwable); if (throwable == null) { // update UI for success } else { // update UI failure } });
If you execute the above code, then it prints the following:
Validation of new user : SUCCESS Registration of new user : success Result : null Throwable : null
Note that the result is null
, because the CompletableFuture
returned by allOf
is always of void type and doesn’t return the result of the provided CompletableFuture
instances.
2.2. allOf example with exception
If there are any exceptions in the provided CompletableFuture
, then the CompletableFuture
returned by allOf
throws CompletionException
.
// first CompletableFuture CompletableFuture<Boolean> validateNewUser = CompletableFuture.supplyAsync(() -> { try { // validate new user details // inducing sleep for demo Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } boolean validationFailed = true; if (validationFailed) { throw new NullPointerException(); } return true; });
CompletableFuture<Void> finalCompletableFuture = CompletableFuture.allOf(validateNewUser, registerNewUser); finalCompletableFuture.whenComplete((result, throwable) -> { System.out.println("Result : " + result); System.out.println("Throwable : " + throwable); if (throwable == null) { // update UI for success } else { // update UI failure } });
Now, if you execute the code again, then the throwable
of the whenComplete
captures the exception and prints.
Result : null Throwable : java.util.concurrent.CompletionException: java.lang.NullPointerException
The finalCompletableFuture
also throws the same exception having it cause within the CompletationException
:
For example, we are using the join()
method to wait synchronously for the finalCompletableFuture
.
CompletableFuture<Void> finalCompletableFuture = CompletableFuture.allOf(validateNewUser, registerNewUser); try { System.out.println(finalCompletableFuture.join()); } catch (CompletionException e) { e.printStackTrace(); }
If you execute the above code, then the CompletionException
is thrown by the finalCompletableFuture
with cause java.lang.NullPointerException
(exception throws by the provided CompletableFuture
).
java.util.concurrent.CompletionException: java.lang.NullPointerException at java.base/java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:314) at java.base/java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:319) at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1766) at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1756) at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290) at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1016) at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1665) at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1598) at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183) Caused by: java.lang.NullPointerException at com.tedblob.java.CompletableFuture.CompletableFutureAllOf.lambda$main$0(CompletableFutureAllOf.java:20)
3. Conclusion
In this article, we have learned the purpose of the allOf
method of the CompletableFuture
.