javaasynchronouscompletable-futurecompletion-stage

Why thenCombine result doesn't complete exceptionally if returning a failed stage?


The following code snippet, which invokes thenCombine, does not set an exception at whenComplete (it prints No exception):

    CompletableFuture.completedFuture(true)
    .thenCombine(CompletableFuture.completedFuture(true),
        (x,y) -> {
        return CompletableFuture.failedStage(new RuntimeException());
    })
    .whenComplete( (myVal, myEx) -> {
      if (myEx == null) {
         System.out.println("No exception");
      } else {
         System.out.println("There was an exception");
      }
    });

However, the similar code below, which invokes thenCompose, does set an exception:

    CompletableFuture.completedFuture(true)
    .thenCompose(
        x -> {
          return CompletableFuture.failedStage(new RuntimeException());
        })
    .whenComplete( (myVal, myEx) -> {
      if (myEx == null) {
        System.out.println("No exception");
      } else {
        System.out.println("There was an exception");
      }
    });

Why is thenCombine returning a normally-completed CompletionStage when its BiFunction is actually returning a failed stage?


Solution

  • In your first example CompletableFuture.failedStage(new RuntimeException()) is the result. The CompletableFuture returned by thenCombine is completed with CompletableFuture.failedStage(new RuntimeException()).

    It gets clearer when you don't chain the calls:

    CompletableFuture<Boolean> first = CompletableFuture.completedFuture(true);
    CompletableFuture<Boolean> second = CompletableFuture.completedFuture(true);
            
    CompletableFuture<CompletionStage<?>> yourQuestion =
            first.thenCombine(second, (x, y) ->
                    CompletableFuture.failedStage(new RuntimeException()));
    

    For your second example you need to read the documentation of thenCompose carefully:

    Returns a new CompletionStage that is completed with the same value as the CompletionStage returned by the given function.

    In the function for thenCompose you return a failed CompletableFuture and its result is used as the result for the CompletableFuture returned by thenCompose: a RuntimeException.