【问题标题】:Shortcut chain of completablefuture based on a condition基于条件的completablefuture的快捷链
【发布时间】:2018-06-17 03:14:14
【问题描述】:

我想跳过基于特定条件的可完成期货链。我尝试了chaining several completionstage 提出的解决方案,但它似乎不起作用。代码如下:

@RunWith(JUnit4.class)
public class ExceptionHandlingTests {
@Test
public void test1() {
    CompletableFuture<Integer> result = new CompletableFuture<>();
    CompletableFuture.runAsync(() -> {
        System.out.println("Completing result1. Result: " + result.isDone());
        result.complete(10);
    }).thenCompose(x -> {
        System.out.println("Completing result2. Result: " + result.isDone());
        result.complete(10);
        return CompletableFuture.completedFuture(5);
    }).thenCompose(x -> {
        System.out.println("Completing result3. Result: " + result.isDone());
        result.complete(10);
        return CompletableFuture.completedFuture(5);
    }).applyToEither(result, Function.identity());
    }
} 

输出:

Completing result1. Result: false
Completing result2. Result: true
Completing result3. Result: true

即使“结果”completablefuture 被标记为已完成,后续的 completablefuture 仍会执行。如何跳过 Completablefuture 2 和 3?

【问题讨论】:

  • 您为什么希望它们被跳过?这些阶段与result 完全无关。
  • 我认为这就是 applyToEither 基于早期堆栈溢出问题所做的。我在描述中提供了链接。在这种情况下,有没有办法跳过其余的可完成期货?
  • 在链接的答案中,有两个期货,只有一个会完成。由于另一个从未完成,因此不会评估其相关阶段。 applyToEither的属性就是不等待对方。但仅applyToEither 不足以阻止对阶段的评估。

标签: java-8 completable-future


【解决方案1】:

你已经创建了一个这样的依赖链:

  first
    ↓  (↘)
   next  result
    ↓  ↙
  final

(↘) 是一个显式的完成调用 result.complete(…),但所有其他完成都是自动发生的。通过applyToEither 创建的final 阶段将使用任一先决条件完成,以先完成者为准,但不会修改它们的行为。

原则上,任何代码都可以在其上调用complete,而不会影响可能完成final 阶段的这些阶段中的任何一个。这同样适用于取消。在舞台上调用cancel 将完成该舞台,即您调用该方法,而不会影响您用于构造它的舞台。

链接问题的答案会创建类似的阶段

    first
   (↙)  (↘)
 next 1  result
   ↓     |
 next 2  |
   ↘    ↙
    final

关键是,初始阶段将显式完成两个CompletableFutures 中的任何一个,不会触发自动完成。其他相关阶段链永远不会被评估。由于final 阶段是applyToEither,因此仅完成其中一个链就足以评估最终功能。它仍然不会影响先决条件阶段。

请注意,对于包含thenCompose 操作的链,您可以以更简单的方式实现类似的逻辑,因为您的函数无论如何都会返回CompletableFuture。因此,只需返回一个新的CompletableFuture,当您想要快捷方式时,该CompletableFuture 将永远不会完成,即可解决问题。你甚至可以改写你最初的runAsync 以使用thenCompose

for(int shortCutAt: IntStream.range(0, 4).toArray()) {
    System.out.println("Example execution with "
                      +(shortCutAt==0? "no shortcut": "shortcut at "+shortCutAt));

    CompletableFuture<Integer> result = new CompletableFuture<>();
    CompletableFuture.completedFuture(null).thenCompose(justVoid -> { // runAsync
        System.out.println("Completing result1. Result: " + result.isDone());
        if(shortCutAt == 1) { result.complete(10); return new CompletableFuture<>(); }
        return CompletableFuture.completedFuture(justVoid);
    }).thenCompose(x -> {
        System.out.println("Completing result2. Result: " + result.isDone());
        if(shortCutAt == 2) { result.complete(10); return new CompletableFuture<>(); }
        return CompletableFuture.completedFuture(5);
    }).thenCompose(x -> {
        System.out.println("Completing result3. Result: " + result.isDone());
        if(shortCutAt == 3) { result.complete(10); return new CompletableFuture<>(); }
        return CompletableFuture.completedFuture(5);
    })
    .applyToEither(result, Function.identity())
    .thenAccept(fr -> System.out.println("final result: "+fr));

    System.out.println();
}
Example execution with no shortcut
Completing result1. Result: false
Completing result2. Result: false
Completing result3. Result: false
final result: 5

Example execution with shortcut at 1
Completing result1. Result: false
final result: 10

Example execution with shortcut at 2
Completing result1. Result: false
Completing result2. Result: false
final result: 10

Example execution with shortcut at 3
Completing result1. Result: false
Completing result2. Result: false
Completing result3. Result: false
final result: 10

【讨论】:

  • 太棒了。谢谢霍尔格。
  • 谢谢@Holger。我重构了解决方案以提高可重用性。将该方法封装在一个名为 ShortCircuitCF 的简单类中,其中包含传播结果或完成可完成未来的方法。这是gist
猜你喜欢
  • 2021-09-16
  • 1970-01-01
  • 2013-08-15
  • 1970-01-01
  • 2021-04-20
  • 1970-01-01
  • 2011-07-01
  • 2021-07-17
  • 1970-01-01
相关资源
最近更新 更多