Why does Java 8 CompletableFuture thenCompose throw a different exception depending on the completion order?

I came across the strange behavior of the Java 8 method CompletableFuture thenCompose. I have two tests that differ only in the order of execution. Both tests simulate a failure in the CompletableFuture generated in thenCompose.

@Test public void completedAfter() { CompletableFuture<String> future1 = new CompletableFuture<>(); CompletableFuture<String> future2 = new CompletableFuture<>(); future1.thenCompose(x -> future2).whenComplete((r, e) -> System.out.println("After: " + e)); future1.complete("value"); future2.completeExceptionally(new RuntimeException()); } @Test public void completedBefore() { CompletableFuture<String> future1 = new CompletableFuture<>(); CompletableFuture<String> future2 = new CompletableFuture<>(); future1.complete("value"); future2.completeExceptionally(new RuntimeException()); future1.thenCompose(x -> future2).whenComplete((r, e) -> System.out.println("Before: " +e)); } 

Output:

 After: java.util.concurrent.CompletionException: java.lang.RuntimeException Before: java.lang.RuntimeException 

The question is why the exception is thrown in a CompletionException in one case, but not in another?

Update: An error report is linked here . It has been flagged and resolved as a bug in the JDK.

+7
java java-8 future
source share
1 answer

Looks like an error in the jdk library.

In the case of After, .thenCompose adds the completion of ThenCopy node to the target future, the execution of which is later launched by .completeExceptionally . The node run completion method finds an exception in the future and raises a .internalComplete at the destination, which .internalComplete all exceptions to a CompletionException . See here how node is created, and here where wrap occurs.

Now, in the case of Before , the code path is completely different. Since the future is already completed, .thenCompose does not create additional nodes, but calls the callback immediately and simply returns (the second future is already completed), after which you call .whenComplete , which, again, does not interfere with creating a new node completion, but simply calls the reverse a challenge right away, giving him an initial exception to the second future.

Boy, looking at this code, there are so many examples that I want to show my students what they should never do ...

+2
source share

All Articles