frblazquez
frblazquez

Reputation: 177

CompletableFuture controlled way abort on exception

Let's suppose I have a CompletableFuture with a couple of stages chained:

processedFuture = someCompletableFuture
    .thenApply(<stage1>)
    .thenApply(<stage2>)

And let's assume that in case an error happens in <stage1> we would like to abort the execution of <stage2> and return SOME_DEFAULT_VALUE. Given the following options:

// Option A
val mayFailFuture = someCompletableFuture
    .thenApply(<stage1>);

if (mayFailFuture.isCompletedExceptionally()) {
    log.error(...);
    return SOME_DEFAULT_VALUE;
}

processedFuture = mayFailFuture.thenApply(<stage2>)
// Option B
processedFuture = someCompletableFuture
    .thenApply(<stage1>)              // returns CompletableFuture<T>
    .exceptionally(<exceptionally1>)  // must return CompletableFuture<T>
    .thenApply(<stage2>)

Is Option A the correct way to abort a chained execution of stages?
In Option B is there any way to abort the execution and return SOME_DEFAULT_VALUE?

Upvotes: 1

Views: 90

Answers (1)

Thomas Timbul
Thomas Timbul

Reputation: 1733

To return your default value when stage1 completes exceptionally, you could consider using handle(BiConsumer):

processedFuture = someCompletableFuture
    .thenApply(<stage1>)
    .handle((s1Result, s1Exception) -> {
        if(s1Exception!=null) return SOME_DEFAULT_VALUE; //Exception was thrown in S1
        else return stage2.apply(s1Result); //apply stage2 otherwise
    })

Edit:

Michael already posted a better solution in comments, which is to apply exceptionally at the end:

processedFuture = someCompletableFuture
    .thenApply(<stage1>)
    .thenApply(<stage2>)
    .exceptionally(e -> SOME_DEFAULT_VALUE);

Just bear in mind that stage2 never executes if stage1 throws an Exception, in which case that same Exception is propagated to exceptionally.

Edit 2:

As per comment, if you wish to return different default values depending on which stage failed, this should do the trick:

processedFuture = someCompletableFuture
    .thenApply(<stage1>)
    .handle((s1Result, s1Exception) -> {
        if(s1Exception!=null) return DEFAULT_VALUE_1; //Exception was thrown in S1
        else return stage2.apply(s1Result); //apply stage2 otherwise
    })
    .exceptionally(e -> DEFAULT_VALUE_2); //treat exceptional return from stage2

Upvotes: 1

Related Questions