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

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

我遇到了 Java 8 CompletableFuture 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));
}

输出为:

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

问题是,为什么在一种情况下异常包裹在 CompletionException 而在另一种情况下却没有?

更新:Here 是相关的错误报告。它已被标记并解决为 JDK.

中的错误

似乎是 jdk 库中的错误。

在"After"情况下,.thenCompose向目标未来添加一个ThenCopy完成节点,其执行稍后由.completeExceptionally触发。完成节点的 run 方法在 future 上找到异常,并在目标上调用 .internalComplete,将所有异常包装到 CompletionException 中。 请参阅 here how the node is created, and here 了解包装发生的位置。

现在,在Before的情况下,代码路径完全不同了。因为 future 已经完成,.thenCompose 不会创建额外的节点,而是立即 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,给它第二个 future 的原始异常。

孩子,看看这段代码,我想向我的学生展示他们不应该做的事情的例子太多了...