Search code examples
javacompletable-future

CompletableFuture compose for exceptions


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.


Solution

  • 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.

    Edit: for Java 12+ there is now exceptionallyCompose(), which I described as an answer on this question’s duplicate target.