需要说明,applyAsync和嵌套的allOf将CompletionStage视为已完成

时间:2018-02-09 13:08:27

标签: java java-8 completable-future

在我的应用程序中,我有3个未来的调用,它们是并行完成的,当收到其中一个的响应时,我还有另外3个请求,所有这些都应该在继续执行代码之前完成,正是DeferredResult从春天开始。

过了一会儿,我意识到有时会在后3个请求完成之前呈现页面。原始源代码(为简单起见,省略逻辑):

public DeferredResult<String> someControllerMethod() {

    DeferredResult<String> result = new DeferredResult();

    CompletableFuture.allOf(
        future1(),
        future2(),
        future3()
    )
    .whenComplete((aVoid, throwable) -> result.setResult("something"));

    return result;
}

public CompletableFuture<?> future3() {
    return someService.asyncCall()
        .thenApplyAsync(response -> {
            ....
            return CompletableFuture.allOf(
                future4(),
                future5(),
                future6()
            );
        }
    );
}

thenApplyAsync有时DeferredResult在实际未来之前完成,而更改为thenComposeAsync似乎可以解决问题。有人能解释我为什么吗?或者它是我的代码中的一个错误,它应该不这样做?

1 个答案:

答案 0 :(得分:2)

thenApply[Async]接受一个计算为任意值的函数。返回值后,将使用该值完成将来的操作。当函数(如在代码中)返回另一个未来时,这不会为其添加额外的含义,将来将是结果值,无论是否已完成,就像任何其他对象一样。

事实上,你的

public CompletableFuture<Void> future3() {
    return someService.asyncCall()
        .thenApplyAsync(response -> {
            ....
            return CompletableFuture.allOf(
                future4(),
                future5(),
                future6()
            );
        }
    );
}

方法甚至不编译,结果是CompletableFuture<CompletableFuture<Void>>,未来的结果值是另一个未来。唯一不发现错误的方法是使用更广泛的类型,例如CompletableFuture<Object>CompletableFuture<?>,作为future3()的返回类型。

相比之下,thenCompose[Async]期望一个能够评估另一个未来的函数,达到您期望的结果。这是“应用”和“撰写”之间的根本区别。如果保留CompletableFuture<Void>的特定future3()返回类型,编译器已经指导您使用“撰写”,因为只有这将被接受。

public CompletableFuture<Void> future3() {
    return someService.asyncCall()
        .thenComposeAsync(response -> {
            ....
            return CompletableFuture.allOf(
                future4(),
                future5(),
                future6()
            );
        }
    );
}