【问题标题】:Returning CompletableFuture<Void> in a conditional function在条件函数中返回 CompletableFuture<Void>
【发布时间】:2019-12-06 21:24:05
【问题描述】:

我有一个用例,我必须从一个基于条件组成 2 个 Completable Futures 的函数返回一个 CompletableFuture&lt;Void&gt;

以下是我现在拥有的 -

private CompletableFuture<Void> processSomething(final SomeEvent event) {
    final CompletableFuture<PaginatedQueryList<Detail>> detail = dao.getData(event.getOrderId());
    return detail.thenApply(n -> n.stream()
        .filter(i -> i.getBusinessType().equals(BusinessType.AWESOME))
        .findFirst()
        .filter(i -> i.getLastUpdateEventTimestamp() <= event.getCreationTime())) // This returns Optional<Detail>
        .thenCompose(i -> i
           .map(o -> deleteItem(event,o))
           .orElse(CompletableFuture.completedFuture(null))); // deleteItem is a async call that returns CompletableFuture<Void>
}

社区可以检查并建议任何其他方法吗?
我特别不喜欢明确返回CompletableFuture.completedFuture(null)

【问题讨论】:

  • 对我来说这段代码非常好。当您必须返回一个CompletableFuture 但您只能有条件地得到一个时,您唯一的选择是在另一种情况下返回一个已经完成的未来,但这完全没问题。请注意,它可能更适合 Code Review,因为您的代码已经可以工作了。
  • @DidierL 感谢您的 cmets,这里和下面的建议答案。另外,感谢 Code Review 网站,我不知道这一点,但现在我知道了。谢谢。

标签: java asynchronous java-8 completable-future


【解决方案1】:

thenCompose 的函数中返回已经完成的未来并没有错。正如this answer 中提到的,您还可以使用CompletableFuture.allOf() 来表示一个空的任务列表,以达到相同的结果。

但您可以使用.orElseGet(() -&gt; …) 来避免提前构建已完成的未来,即使在不需要时也是如此。

此外,您可以将thenApply(…).thenCompose(…) 链替换为单个thenCompose(…)

private CompletableFuture<Void> processSomething(final SomeEvent event) {
    CompletableFuture<PaginatedQueryList<Detail>> detail = dao.getData(event.getOrderId());
    return detail.thenCompose(n -> n.stream()
        .filter(i -> i.getBusinessType().equals(BusinessType.AWESOME))
        .findFirst()
        .filter(i -> i.getLastUpdateEventTimestamp() <= event.getCreationTime())
        .map(o -> deleteItem(event, o))
        .orElseGet(() -> CompletableFuture.completedFuture(null)));
// or   .orElseGet(() -> CompletableFuture.allOf()));
// or   .orElseGet(CompletableFuture::allOf));

}

【讨论】:

    【解决方案2】:

    实现它的方法之一是,只需进行两项更改。

    1. Optional&lt;Detail&gt; 作为参数传递给delete 方法并生成 在执行实际逻辑之前对其进行必要的检查。
    2. .thenCompose() 更改为.thenAccept()。它会回来 CompletionStage&lt;Void&gt; 您可以将其转换为 CompletableFuture&lt;Void&gt; 在方法返回的顶部返回之前。

    private CompletableFuture<Void> processSomething(final SomeEvent event) {
        final CompletableFuture<PaginatedQueryList<Detail>> detail = dao.getData(event.getOrderId());
        return (CompletableFuture<Void>)detail.thenApply(n -> n.stream()
            .filter(i -> i.getBusinessType().equals(BusinessType.AWESOME))
            .findFirst()
            .filter(i -> i.getLastUpdateEventTimestamp() <= event.getCreationTime())) // This returns Optional<Detail>
            .thenAccept(i -> deleteItem(event, i).join())); // deleteItem is a async call that returns CompletableFuture<Void>
    }
    
    private CompletableFuture<Void> deleteItem(Optional<Detail> optionalDetail){
         if(optionalDetail.isPresent()){
           // Your existing logic
         }
    }
    

    假设如果您不想更改deleteItem() 方法签名,那么只需这样做,

    .thenAccept(i -> 
         {
          if(i.isPresent()){
             deleteItem(event, i.get());
          }
         });
    

    【讨论】:

    • 如果你使用thenAccept()而不是thenCompose(),返回的未来将在deletItem()完成之前完成。
    • 您可以通过在deleteItem() 上调用join() 来停止thenAccept() 中的当前线程,直到它完成,对吗?还是不应该?
    • 嗯,这会有点破坏目的,因为这意味着您将消耗 2 个线程而不是 1 个。请注意,Optional 并非旨在用作方法参数。此外,isPresent() 后跟 get() 也是一种反模式,或者至少是一种代码异味(有时您无法避免它,至少在 Java 8 中是这样)。只需使用 ifPresent() 代替。您还会注意到,在 else 情况下,您的新 deleteItem() 方法仍需要返回 completedFuture(null)
    • @DidierL 是的,我同意它会消耗 2 个线程。关于反模式,我想我今天学到了一些新东西。感谢您提供这些信息。
    • 是什么让你说thenAccept() 返回CompletionStage 并且必须强制转换? docs.oracle.com/javase/8/docs/api/java/util/concurrent/…
    猜你喜欢
    • 2016-03-17
    • 1970-01-01
    • 1970-01-01
    • 2016-08-28
    • 1970-01-01
    • 1970-01-01
    • 2020-01-18
    • 2019-10-10
    • 2015-01-24
    相关资源
    最近更新 更多