我无法理解thenApply(
)和thenCompose()
之间的区别。
那么,有人可以提供有效的用例吗?
来自Java文档:
thenApply(Function<? super T,? extends U> fn)
返回一个新的
CompletionStage
,当此阶段完成时 通常,以此阶段的结果作为参数执行 提供的功能。
thenCompose(Function<? super T,? extends CompletionStage<U>> fn)
返回一个新的
CompletionStage
,当此阶段完成时 通常,以此阶段作为所提供的参数执行 功能
我得到thenCompose
的第二个参数扩展了thenApply
没有的CompletionStage。
有人可以举例说明我必须使用thenApply
和thenCompose
吗?
答案 0 :(得分:115)
thenApply
。
CompletableFuture<Integer> future =
CompletableFuture.supplyAsync(() -> 1)
.thenApply(x -> x+1);
如果您有异步映射函数(即返回thenCompose
的函数),则使用 CompletableFuture
。然后它将直接返回结果的未来,而不是嵌套的未来。
CompletableFuture<Integer> future =
CompletableFuture.supplyAsync(() -> 1)
.thenCompose(x -> CompletableFuture.supplyAsync(() -> x+1));
答案 1 :(得分:36)
Java 9中更新的Javadoc可能有助于更好地理解它:
<U> CompletionStage<U> thenApply(Function<? super T,? extends U> fn)
返回一个新的
CompletionStage
,当此阶段完成时 通常,以此阶段的结果作为参数执行 提供的功能。此方法类似于
Optional.map
和Stream.map
。有关规则,请参阅
CompletionStage
文档 特殊的完成。
<U> CompletionStage<U> thenCompose(Function<? super T,? extends CompletionStage<U>> fn)
返回使用相同内容完成的新
CompletionStage
值为给定函数返回的CompletionStage
。当此阶段正常完成时,将调用给定的函数 这个阶段的结果作为论证,返回另一个
CompletionStage
。当那个阶段正常完成时, 此方法返回的CompletionStage
已完成相同的操作 值。为确保进度,所提供的功能必须最终安排 完成其结果。
此方法类似于
Optional.flatMap
和Stream.flatMap
有关规则,请参阅
CompletionStage
文档 特殊的完成。
答案 2 :(得分:22)
我认为@Joe C发布的回答是误导性的。
让我试着用一个例子解释thenApply
和thenCompose
之间的区别。
我们假设我们有两种方法:getUserInfo(int userId)
和getUserRating(UserInfo userInfo)
:
public CompletableFuture<UserInfo> userInfo = getUserInfo(userId)
public CompletableFuture<UserRating> getUserRating(UserInfo)
两种方法返回类型都是CompletableFuture
。
我们希望首先致电getUserInfo()
,并在完成后,使用生成的getUserRating()
致电UserInfo
。
完成getUserInfo()
方法后,让我们同时尝试thenApply
和thenCompose
。区别在于返回类型:
CompletableFuture<CompletableFuture<UserRating>> f =
userInfo.thenApply(this::getUserRating);
CompletableFuture<UserRating> relevanceFuture =
userInfo.thenCompose(this::getUserRating);
thenCompose()
就像Scala's flatMap
一样,可以展平嵌套的未来。
thenApply()
按原样返回嵌套的期货,但是thenCompose()
压扁了嵌套的CompletableFutures
,因此更容易将更多方法调用链接到它。
答案 3 :(得分:7)
thenApply
上调用 thenCompose
和CompletableFuture
,并通过提供Function
对结果进行一些处理。 thenApply
和thenCompose
各自返回一个CompletableFuture
作为自己的结果,因此您可以链接多个thenApply
或thenCompose
,每个都有一个{{1 }}对最后一个Function
的结果进行处理。
此Function
有时需要同步执行某些操作,然后返回结果,在这种情况下,应使用Function
。
thenApply
您还可以在此CompletableFuture.completedFuture(1)
.thenApply((x)->x+1) // adding one to the result synchronously
.thenApply((x)->System.println(x));
中执行异步操作,并且您执行的异步操作应返回一个Function
。链中的下一个CompletionStage
对获取Function
作为输入并不感兴趣,而是对该CompletionStage
的结果感兴趣。因此,您应该使用CompletionStage
。
thenCompose
在Javascript中,// 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
.thenApply((x)->System.println(x));
可以接受一个函数,该函数可以返回值或值的Promise.then
。在Java中,由于类型规则,必须对两个函数进行明确的类型化,即。 Promise
和(Function<? super T,? extends U> fn
。 (或者,如果返回Function<? super T,? extends CompletionStage<U>> fn
,Java必须做某种类型检查以做一些特殊的事情,但是他们选择了前者)最终结果是CompletionStage
分两部分实现Promise.then
和thenApply
。
如果您对thenCompose
感到困惑,也可以阅读my answer。
答案 4 :(得分:0)
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;
}
其他可视化两者之间差异的问题
当您不知道必须应用多少时间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;
}
答案 5 :(得分:0)
JoeC 的答案是正确的,但我认为可以明确 thenCompose 目的的更好比较是 thenApply 和 之间的比较>然后申请!一次是将同步映射传递给它,一次是将异步映射传递给它。
如果传递给 thenApply 的映射返回一个 String(非未来,所以映射是同步的),那么它的结果将是 CompletableFuture<String>
.现在类似地,当传递给它的映射返回一个 CompletableFutureCompletableFuture<CompletableFuture<String>>
,这是不必要的嵌套(未来的未来仍然是未来!)。在这里,我们可以使用 thenCompose 来“组合”(嵌套)多个异步任务,而不会在结果中嵌套未来。