You can use the following methods to handle exceptions in Java 8+ CompletableFuture
Java 8+
handle, andhandleAsyncJava 8+
exceptionally, and Java 12+exceptionallyAsyncJava 8+
whenComplete, andwhenCompleteAsync
The stage created by exceptionally/exceptionallyAsync and whenComplete/whenCompleteAsync can only be executed when there're exceptions in the previous stages, while by handle/handleAsync, it can be executed in both normal and exception cases
Let's walk through this tutorial to explore them in more details
Use handle(BiFunction) and handleAsync(BiFunction)
handle and handleAsync are used for handling exceptions and translate completion outcomes. The stage created by them can be executed in either the previous stages have exceptions or not
They accept a BiFunction with (result, exception) as arguments received from the preceding stage, and complete its stage with the return value from BiFunction
In the following example, handleAsync received NumberFormatException caused by Integer.parseInt("s") in supplyAsync, it prints out the error message and returns Error! to the next stage thenAcceptAsync
CompletableFuture completableFuture = CompletableFuture
.supplyAsync(() -> Integer.parseInt("s"))
.handleAsync((result, e) -> {
if (e != null) {
System.out.println(e.getMessage());
return "Error!";
} else {
return result + 1;
}
})
.thenAcceptAsync(System.out::println);
Output
java.lang.NumberFormatException: For input string: "s"
Error!
In this example, handleAsync is still executed, although there are no exceptions in the previous stages
CompletableFuture completableFuture = CompletableFuture
.supplyAsync(() -> 1)
.handleAsync((result, e) -> {
if (e != null) {
System.out.println(e.getMessage());
return "Error!";
} else {
return result + 1;
}
})
.thenAcceptAsync(System.out::println);
Output
2
Use exceptionally(Function) and exceptionallyAsync(Function)
Unlike handle and handleAsync, the stage created by exceptionally and exceptionallyAsync only executed when there're exceptions in the previous stages. Apart from that, the return value from them have to be null or the same type as the preceding stage
exceptionally and exceptionallyAsync accept a Function with (exception) as the only argument
In the below example, exceptionallyAsync received NumberFormatException caused by Integer.parseInt("s") in supplyAsync, it prints out the error message and returns 0 to the next stage thenAcceptAsync
CompletableFuture completableFuture = CompletableFuture
.supplyAsync(() -> Integer.parseInt("s"))
.exceptionallyAsync((e) -> {
System.out.println(e.getMessage());
return 0;
})
.thenAcceptAsync(System.out::println);
Output
java.lang.NumberFormatException: For input string: "s"
0
In this example, exceptionallyAsync is not executed, as there're no exceptions from previous stages
CompletableFuture completableFuture = CompletableFuture
.supplyAsync(() -> 1)
.exceptionallyAsync((e) -> {
System.out.println(e.getMessage());
return 0;
})
.thenAcceptAsync(System.out::println);
Output
1
Use whenComplete(BiConsumer) and whenCompleteAsync(BiConsumer)
whenComplete and whenCompleteAsync accept a BiConsumer with (result, exception) as the arguments
Different from handle and handleAsync, whenComplete and whenCompleteAsync are not used for translating completion outcomes (return no value). The stage created by them are only executed when there're exceptions in the previous stages
In the following example, whenCompleteAsync received NumberFormatException caused by Integer.parseInt("s") in supplyAsync, and prints out the error message
CompletableFuture completableFuture = CompletableFuture
.supplyAsync(() -> Integer.parseInt("s"))
.whenCompleteAsync((result, e) -> {
if (e != null) System.out.println(e.getMessage());
})
.thenAcceptAsync(System.out::println);
Output
java.lang.NumberFormatException: For input string: "s"
In this example, whenCompleteAsync is not executed, as there're no exceptions from previous stages
CompletableFuture completableFuture = CompletableFuture
.supplyAsync(() -> 1)
.whenCompleteAsync((result, e) -> {
if (e != null) System.out.println(e.getMessage());
})
.thenAcceptAsync(System.out::println);
Output
1
Conclusion
In this article, we learned to handle exceptions in Java CompletableFuture by using handle, handleAsync, exceptionally, exceptionallyAsync, whenComplete and whenCompleteAsync. You can find the full source code as below