java 8 – CompletableFuture completeExceptionally example

Let’s see what happens when async calls exception is raised and how we can handle this situation.


Suppose we have a method which makes asynchronous call:

public Future doOperationAsync() {
        CompletableFuture futureObj = new CompletableFuture<>();
        new Thread(() -> {
            String result = someOperation();
            futureObj.complete(result);
        }).start();
        return futureObj;
}

if someOperation() method is running fine, we don’t have any issues, doOperationAsync() will execute and return the Future object. But what happens if someOperation() throws exception during execution time.

private String someOperation() {
        throw new RuntimeException("Operation is not successful");
}

Unfortunately in this case raised exception will remain in the thread. As a result the caller will be blocked forever. One solution is to use the ovevrload method of get method with a timeout, which will allow at least not be blocked forever and exit with TimeoutException.

Future result = new CompletableExample().doOperationAsync();
result.get(4, TimeUnit.SECONDS);
[addToAppearHere]

However cleaner way would be to use CompletableFuture.completeExceptionally() method. In this case the caller will receive ExecutionException, which will contain also the original exception.

public Future doOperationAsync() {
        CompletableFuture futureObj = new CompletableFuture<>();
        new Thread(() -> {
            try {
                String result = someOperation();
                futureObj.complete(result);
            } catch (Exception e) {
                futureObj.completeExceptionally(e);
            }
        }).start();
        return futureObj;
}

Output will be

java.util.concurrent.ExecutionException: java.lang.RuntimeException: Operation is not successful
	at java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:357)
	at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1895)
	at com.example.CompleteExceptionallyExample.main(CompleteExceptionallyExample.java:12)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:497)
	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
Caused by: java.lang.RuntimeException: Operation is not successful
	at com.example.CompleteExceptionallyExample.someOperation(CompleteExceptionallyExample.java:32)
	at com.example.CompleteExceptionallyExample.lambda$doOperationAsync$0(CompleteExceptionallyExample.java:22)
	at java.lang.Thread.run(Thread.java:745)