CompletableFuture | 然后应用vs thenCompose

Guy*_*uyT 91 java java-8 completable-future

我无法理解thenApply(和之间的区别thenCompose().

那么,有人可以提供有效的用例吗?

来自Java文档:

thenApply(Function<? super T,? extends U> fn)
Run Code Online (Sandbox Code Playgroud)

返回一个新的CompletionStage,当该阶段正常完成时,将使用此阶段的结果作为所提供函数的参数执行.

thenCompose(Function<? super T,? extends CompletionStage<U>> fn)
Run Code Online (Sandbox Code Playgroud)

返回一个新的CompletionStage,当此阶段正常完成时,将使用此阶段作为所提供函数的参数执行.

我得到的第二个参数thenCompose扩展了CompletionStage,thenApply而不是.

有人可以提供一个例子,我必须使用thenApply何时使用thenCompose

Joe*_*e C 134

thenApply 如果您具有同步映射功能,则使用此选项.

CompletableFuture<Integer> future = 
    CompletableFuture.supplyAsync(() -> 1)
                     .thenApply(x -> x+1);
Run Code Online (Sandbox Code Playgroud)

thenCompose如果你有一个异步映射函数(即返回一个CompletableFuture),则使用它.然后它将直接返回结果的未来,而不是嵌套的未来.

CompletableFuture<Integer> future = 
    CompletableFuture.supplyAsync(() -> 1)
                     .thenCompose(x -> CompletableFuture.supplyAsync(() -> x+1));
Run Code Online (Sandbox Code Playgroud)

  • 他们不会这样做.但是,如果他们使用的第三方库返回了一个`CompletableFuture`,那么这就是`thenCompose`将展平结构的方法. (8认同)
  • 为什么程序员应该使用`.thenCompose(x - > CompletableFuture.supplyAsync(() - > x + 1))`而不是`.thenApplyAsync(x - > x + 1)`?同步或异步是*不是相关的差异. (7认同)

Did*_*r L 39

Java 9中更新的Javadoc可能有助于更好地理解它:

thenApply

<U> CompletionStage<U> thenApply?(Function<? super T,? extends U> fn)

返回一个新的CompletionStage,当该阶段正常完成时,将使用此阶段的结果作为所提供函数的参数执行.

这种方法类似于Optional.mapStream.map.

有关CompletionStage特殊完成的规则​​,请参阅文档.

thenCompose

<U> CompletionStage<U> thenCompose?(Function<? super T,? extends CompletionStage<U>> fn)
Run Code Online (Sandbox Code Playgroud)

返回一个新的CompletionStage,它与CompletionStage给定函数返回的值相同.

当这个阶段正常完成时,调用给定的函数,将此阶段的结果作为参数,返回另一个 CompletionStage.当该阶段正常完成时, CompletionStage此方法返回的值将以相同的值完成.

为确保进度,所提供的功能必须安排最终完成其结果.

这种方法类似于Optional.flatMapStream.flatMap.

有关CompletionStage特殊完成的规则​​,请参阅文档.

  • 我想知道他们为什么不首先将这些函数命名为`map`和`flatMap`. (13认同)

小智 34

我认为@Joe C发布的回答是误导性的.

让我试着解释一下thenApplythenCompose示例之间的区别.

我们假设我们有两种方法:getUserInfo(int userId)getUserRating(UserInfo userInfo):

public CompletableFuture<UserInfo> userInfo = getUserInfo(userId)

public CompletableFuture<UserRating> getUserRating(UserInfo)
Run Code Online (Sandbox Code Playgroud)

两种方法返回类型都是CompletableFuture.

我们想先打电话getUserInfo(),在完成后,打电话getUserRating()给结果UserInfo.

在完成getUserInfo()方法时,让我们尝试两个thenApplythenCompose.不同之处在于退货类型:

CompletableFuture<CompletableFuture<UserRating>> f =
    userInfo.thenApply(this::getUserRating);

CompletableFuture<UserRating> relevanceFuture =
    userInfo.thenCompose(this::getUserRating);
Run Code Online (Sandbox Code Playgroud)

thenCompose()就像ScalaflatMap那样可以平息嵌套的未来.

thenApply()按原样返回嵌套的期货,但是thenCompose()扁平嵌套,CompletableFutures以便更容易将更多的方法调用链接到它.

  • 那么乔 C 的回答并没有误导。这是正确的,更简洁。 (12认同)
  • 这非常有帮助:-) (2认同)
  • 恕我直言,编写 CompletableFuture&lt;UserInfo&gt; getUserInfo 和 CompletableFuture&lt;UserRating&gt; getUserRating(UserInfo) \\ 相反,如果我想异步和链式使用它,它应该是 UserInfo getUserInfo() 和 int getUserRating(UserInfo) ,那么我可以使用 ompletableFuture.supplyAsync(x =&gt; getUserInfo(userId)).thenApply(userInfo =&gt; getUserRating(userInfo)) 或类似的东西,恕我直言,它更具可读性,并且不必将所有返回类型包装到 CompletableFuture 中 (2认同)

128*_*822 8

thenApplythenCompose是的方法CompletableFuture。当你打算做些什么来使用他们CompleteableFuture的一个结果Function

thenApply并且thenCompose都返回一个CompletableFuture作为自己的结果。您可以链接多个thenApply或链接thenCompose在一起。Function为每个调用提供a ,其结果将作为next的输入Function

Function你提供的,有时需要同步做一些事情。您的返回类型Function应为非Future类型。在这种情况下,您应该使用thenApply

CompletableFuture.completedFuture(1)
    .thenApply((x)->x+1) // adding one to the result synchronously, returns int
    .thenApply((y)->System.println(y)); // value of y is 1 + 1 = 2
Run Code Online (Sandbox Code Playgroud)

其他时候,您可能希望在this中进行异步处理Function。在这种情况下,你应该使用thenCompose。您的返回类型Function应为CompletionStageFunction链中的下一个将获得该结果CompletionStage作为输入,从而展开CompletionStage

// addOneAsync may be implemented by using another thread, or calling a remote method
// CompletableFuture<Integer> addOneAsync(int input);
CompletableFuture.completedFuture(1)
    .thenCompose((x)->addOneAsync(x)) // doing something asynchronous, returns CompletableFuture<Integer>
    .thenApply((y)->System.println(y)); // y is an Integer, the result of CompletableFuture<Integer> above
Run Code Online (Sandbox Code Playgroud)

这与Javascript的想法类似PromisePromise.then可以接受返回值或值a Promise的函数。差异是由Java的通用擦除引起的。Function<? super T,? extends U> fn并且Function<? super T,? extends CompletionStage<U>> fn被视为相同的运行时类型- Function。因此thenApplythenCompose必须被明确命名,否则Java编译器会抱怨相同的方法签名。最终结果是,Javascript Promise.then是由Java thenApplythenComposeJava的两部分实现的。

如果您也对相关功能感到困惑,可以阅读我的其他答案thenApplyAsync


Mag*_*GGG 5

thenCompose()更适合链接 CompletableFuture。

thenApply()更适合 Completable future 的转换结果。

您可以使用这两种技术来实现您的目标,但其中一种技术比其他技术更适合一种用例。

public CompletableFuture<Integer> process(Integer i) {
    CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(
            () -> new HeavyTask(i).execute());
    return completableFuture;
}

@SneakyThrows
public CompletableFuture<Integer> thenApplyVsThenCompose() {
    // each time calling thenApply() function returns CompletionState
    // so you will get nested Futures 
    // you can think about it like map() java optional
    CompletableFuture<Future<Integer>> cf1 = CompletableFuture.supplyAsync(
            () -> new HeavyTask().execute())
            .thenApply(i -> process(i));

    // to get result you will have to get nested get() calls
    Integer resultFromThenApply = cf1.get().get();

    // when calling thenCompose() nested Futures are flatten
    // you can think about it like flatMap() java optional
    CompletableFuture<Integer> cf2;
    cf2 = CompletableFuture.supplyAsync(
            () -> new HeavyTask().execute())
            .thenCompose(this::process);

    // you have to just call one get() since thenCompose was flatten
    Integer resultFromThenCompose = cf2.get();
    return null;
} 
Run Code Online (Sandbox Code Playgroud)

其他可以形象化两者之间差异的问题

当您不知道必须应用 thenApply()/thenCompose() 多少次(例如递归方法)时,您将如何实现解决方案?

public void nested() throws ExecutionException, InterruptedException {
    CompletableFuture<Integer> completableFutureToCompose = CompletableFuture.completedFuture(1);
    for (int i = 0; i < 10; i++) {
        log.info("Composing");
        completableFutureToCompose = completableFutureToCompose.thenCompose(this::process);
    }
    completableFutureToCompose.get();

    // not achievable using then apply
    CompletableFuture<Integer> completableFutureToApply = CompletableFuture.completedFuture(1);
    for (int i = 0; i < 10; i++) {
        log.info("Applying");
        completableFutureToApply = completableFutureToApply.thenApply(this::process).get();
    }
    completableFutureToCompose.get();
}

public CompletableFuture<Integer> process(Integer i) {
    log.info("PROCESSING");
    CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(
            () -> new HeavyTask(i).execute());
    return completableFuture;
}
Run Code Online (Sandbox Code Playgroud)
  • 使用组合,您首先创建收据如何将期货传递给另一个,然后执行
  • 使用 apply 在每次 apply 调用后执行逻辑