CompletableFuture | thenApply与thenCompose


119

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

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

从Java文档中:

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

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

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

返回一个新值CompletionStage,当此阶段正常完成时,将以该阶段作为所提供函数的参数来执行此操作。

我得到的thenCompose扩展的CompletionStage 的第二个参数thenApply没有。

有人可以提供一个例子,说明我必须在什么情况下使用thenApply以及何时使用thenCompose


39
你明白之间的差别map,并flatMapStreamthenApplymapthenComposeflatMapCompletableFuture。您通常thenCompose避免使用CompletableFuture<CompletableFuture<..>>
Misha

2
@Misha感谢您的评论。是的,我知道的区别map,并flatMap和我明白你的意思。再次感谢:)
GuyT '17

这是一个非常不错的指南,从CompletableFuture开始-baeldung.com/java-completablefuture
thealchemist

Answers:


168

thenApply 如果您具有同步映射功能,则使用。

CompletableFuture<Integer> future = 
    CompletableFuture.supplyAsync(() -> 1)
                     .thenApply(x -> x+1);

thenCompose如果您具有异步映射函数(即返回a的函数CompletableFuture),则使用。然后它将直接返回带有结果的Future,而不是嵌套的Future。

CompletableFuture<Integer> future = 
    CompletableFuture.supplyAsync(() -> 1)
                     .thenCompose(x -> CompletableFuture.supplyAsync(() -> x+1));

14
为什么程序员应该使用.thenCompose(x -> CompletableFuture.supplyAsync(() -> x+1))而不是.thenApplyAsync(x -> x+1)?作为同步或异步是相关的差异。
Holger

17
他们不会那样做。但是,如果他们使用的第三方库返回了CompletableFuture,那么这将是thenCompose扁平化结构的地方。
乔C

1
@ArunavSanyal,票数显示不同的图片。这个答案是简洁明了的。
亚历克斯·谢斯特罗夫'18

如果您感到困惑,@ Holger会读我的其他答案,thenApplyAsync因为这与您想像的不一样。
1883822年

@ 1283822我不知道是什么让您认为我感到困惑,并且您的回答中没有任何内容可以支持“这不是您想的那样”的说法。
Holger

49

我认为@Joe C发布的答案具有误导性。

让我试着解释之间的差别thenApply,并thenCompose用一个例子。

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

public CompletableFuture<UserInfo> getUserInfo(userId)

public CompletableFuture<UserRating> getUserRating(UserInfo)

这两种方法的返回类型均为CompletableFuture

我们getUserInfo()要先调用,并在调用完成后调用getUserRating()带有结果的UserInfo

getUserInfo()方法完成后,让我们同时尝试thenApplythenCompose。区别在于返回类型:

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

CompletableFuture<UserRating> relevanceFuture =
    userInfo.thenCompose(this::getUserRating);

thenCompose()ScalaflatMap一样使嵌套的期货变平。

thenApply()返回嵌套的期货,但将其展thenCompose()平,CompletableFutures以便更轻松地链接更多的方法调用。


2
这样乔C的回答就不会产生误导。这是正确且更简洁的。
koleS '18 -10-25

2
这非常有帮助:-)
dstibbe

1
恕我直言,写CompletableFuture <UserInfo> getUserInfo和CompletableFuture <UserRating> getUserRating(UserInfo)\\的设计很糟糕,如果我想异步使用它并应该使用它,则应该是getInfoInfo()和int getUserRating(UserInfo),然后我可以使用ompletableFuture.supplyAsync(x => getUserInfo(userId))。thenApply(userInfo => getUserRating(userInfo))或类似的方法,恕我直言,并且不必将所有返回类型都包装到CompletableFuture中
user1694306

@ user1694306是否为不良设计取决于用户评级是否包含在UserInfo(然后是)中,或者是否必须单独获得,甚至可能是昂贵的(然后是否)。
glglgl

42

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

然后申请

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

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

此方法类似于Optional.mapStream.map

有关CompletionStage涵盖异常完成的规则​​,请参见文档。

然后撰写

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

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

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

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

此方法类似于Optional.flatMapStream.flatMap

有关CompletionStage涵盖异常完成的规则​​,请参见文档。


14
我不知道为什么他们没有名字的那些功能mapflatMap摆在首位。
Matthias Braun

1
@MatthiasBraun我认为这是因为thenApply()将仅调用Function.apply(),并且thenCompose()有点类似于编写函数。
Didier L

14

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

thenApply并且thenCompose都返回一个CompletableFuture作为自己的结果。你可以将多个thenApplythenCompose在一起。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

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

// addOneAsync may be implemented by using another thread, or calling a remote method
abstract 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

这与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实现分为两部分- thenApplythenCompose-在Java中。

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.