The CompletableFuture
API allows us to use thenCompose
to chain another future:
CompletableFuture<String> future1 = submit("foo");
CompletableFuture<String> future2 = future.thenCompose((result) -> submit(result));
This only works for successful responses though. Is there a way to do the same but also including exception handling?
For example:
CompletableFuture<String> future1 = submit("foo");
CompletableFuture<String> future2 = future.handleCompose((result, error) -> {
if (error != null)
return submit("failure"); // Handle error by doing a different action with the same result (like a fallback)
else
return submit(result);
});
I know you can do something like whenComplete
:
CompletableFuture<String> future2 = new CompletableFuture<>();
CompletableFuture<String> future1 = submit("foo");
future.whenComplete((result, error) -> {
CompletableFuture<String> tmp;
if (error != null)
tmp = submit("failure");
else
tmp = submit(result);
tmp.whenComplete((result2, error2) -> {
if (error2 != null) future2.completeExceptionally(error2);
else future2.complete(result2);
});
});
This however loses the ability to cancel properly and seems like a very "hacky" solution compared to the success composition handling. Is there a good way that does not require making my own extension of the CompletableFuture class?
The problem is that I need to return a single future from my method and it should be capable to cancel the entire process at any point in time.
You can combine an handle()
returning a CompletableFuture
with a thenCompose(x -> x)
:
CompletableFuture<String> future2 = future.handle((result, error) -> {
if (error != null)
return submit("failure"); // Handle error by doing a different action with the same result (like a fallback)
else
return submit(result);
})
.thenCompose(x -> x);
Unfortunately, there is no way to avoid that last thenCompose()
. See also How to avoid invoking CompletableFuture.thenCompose(x -> x)
? and my comment on it.
You can make use of CompletableFuture.allOf(..), This is not the only solution but it is better.
public CompletableFuture<String> getSomeDataFromExternalIfPossible() {
var input = "SomeKey";
CompletableFuture<String> externalDataFetchCFuture = getDataFromExternal(input);
return CompletableFuture.allOf(externalDataFetchCFuture)
.exceptionally(
ex -> {
// This exceptionally(...) is required as allOf(...) propagates exception thrown by any of the supplied futures
// Log if you are interested in failed case of externalDataFetchCFuture
return null;
})
.thenCompose(
unused -> {
// Check if externalDataFetchCFuture completed successfully and invoke other Async method if not
if (!externalDataFetchCFuture.isCompletedExceptionally()) {
// On externalDataFetchCFuture successful execution
return externalDataFetchCFuture;
} else {
// On externalDataFetchCFuture failed execution
// Fetching data from DB or from any other integration
return getDataFromExternal2(input);
}
});
}
private CompletableFuture<String> getDataFromExternal(String input) {
return CompletableFuture.completedFuture("abc");
}
private CompletableFuture<String> getDataFromExternal2(String input) {
return CompletableFuture.completedFuture("xyz");
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.