简体   繁体   English

为什么Java 8 CompletableFuture thenCompose会根据完成顺序生成不同的异常?

[英]Why Java 8 CompletableFuture thenCompose generates different exception depending on the order of completion?

I have encountered strange behavior of Java 8 CompletableFuture thenCompose method. 我遇到过Java 8 CompletableFuture thenCompose方法的奇怪行为。 I have two tests that differ only in order of execution. 我有两个测试,仅在执行顺序上有所不同。 Both tests simulate failure in the CompletableFuture generated in thenCompose. 两个测试都模拟thenCompose中生成的CompletableFuture中的失败。

@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));
}

The output is: 输出是:

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

The question is, why is the exception wrapped in CompletionException in one case but not in the other? 问题是,为什么在一个案例中包含在CompletionException中但在另一个案例中却没有包含异常?

Update: Here is related bug report. 更新: 是相关的错误报告。 It has been marked and resolved as a bug in JDK. 它已被标记并解析为JDK中的错误。

Seems like a bug in the jdk library. 好像是jdk库中的一个bug。

In the "After" case, .thenCompose adds a ThenCopy completion node to the target future, whose execution is later triggered by .completeExceptionally . 在“After”情况下, .thenCompose将一个ThenCopy完成节点添加到目标future,其执行稍后由.completeExceptionally触发。 The completion node's run method finds the exception on the future, and calls .internalComplete on the destination, that wraps all exceptions into CompletionException . 完成节点的run方法在将来找到异常,并在目标上调用.internalComplete ,它将所有异常包装到CompletionException See here how the node is created, and here for where the wrapping happens. 请参阅此处如何创建节点,以及此处的包装发生位置。

Now, in the Before case, the code path is completely different. 现在,在Before案例中,代码路径完全不同。 Because the future is already completed, .thenCompose does not create additional nodes, but invokes the callback right away, and simply returns an (already completed second future), on which you then call .whenComplete , which, again, does not bother to create a new completion node, but simply invokes the callback right away, giving it the original exception from the second future. 因为未来已经完成, .thenCompose不会创建额外的节点,而是立即调用回调,然后只返回一个(已经完成的第二个未来),然后调用.whenComplete ,再次,它无需创建一个新的完成节点,但只是立即调用回调 ,从第二个未来给它原始的异常。

Boy, looking at this code, there are so many examples I want to show to my students of what they should never do ... 男孩,看着这段代码,有很多例子我想向学生展示他们应该做的事情......

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM