
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 CompletableFutureget(), 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.
