Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java8 CompletableFuture recoverWith equivalent? eg exceptionally but return CompletableFuture<U>

I don't see an obvious way to handle an exception with an asynchronous result.

For example, if I want to retry an async operation, I would expect something like this:

CompletionStage<String> cf = askPong("cause error").handleAsync((x, t) -> {
    if (t != null) {
        return askPong("Ping");
    } else {
        return x;
    }
});

Where askPong asks an actor:

public CompletionStage<String> askPong(String message){
    Future sFuture = ask(actorRef, message, 1000);
    final CompletionStage<String> cs = toJava(sFuture);
    return cs;
} 

However handleAsync doesn't do what you think it does - it runs the callbacks on another thread asynchronously. Returning a CompletionStage here is not correct.

Jeopardy question of the day: thenApply is to thenCompose as exceptionally is to what?

like image 684
JasonG Avatar asked Feb 21 '15 22:02

JasonG


2 Answers

Is this what you are looking for?

askPong("cause error")
        .handle( (pong, ex) -> ex == null 
                ? CompletableFuture.completedFuture(pong) 
                : askPong("Ping")
        ).thenCompose(x -> x);

Also, do not use the ...Async methods unless you intend for the body of the supplied function to be executed asynchronously. So when you do something like

.handleAsync((x, t) -> {
    if (t != null) {
        return askPong("Ping");
    } else {
        return x;
    })

You are asking for the if-then-else to be run in a separate thread. Since askPong returns a CompletableFuture, there's probably no reason to run it asynchronously.

like image 180
Misha Avatar answered Oct 20 '22 15:10

Misha


Jeopardy question of the day: thenApply is to thenCompose as exceptionally is to what?

I know this was initially java-8, but, since java-12, the answer would be exceptionallyCompose:

exceptionallyCompose[Async](Function<Throwable,? extends CompletionStage<T>> fn [, Executor executor])

Returns a new CompletionStage that, when this stage completes exceptionally, is composed using the results of the supplied function applied to this stage's exception.

As the JavaDoc indicates, the default implementation is:

return handle((r, ex) -> (ex == null)
              ? this
              : fn.apply(ex))
    .thenCompose(Function.identity());

That is, using handle() to call the fallback, and thenCompose() to unwrap the resulting nested CompletableFuture<CompletableFuture<T>> – i.e., what you would have done in previous versions of Java (like in Misha’s answer), except you would have to replace this with completedFuture(r).

like image 33
Didier L Avatar answered Oct 20 '22 14:10

Didier L