基于条件的Completablefuture的快捷链

时间:2018-01-08 06:28:15

标签: java-8 completable-future

我想根据具体情况跳过完整的未完成链接链。我尝试了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

即使&#34;结果&#34; completablefuture已标记完成,后续的可完成期限仍在执行中。如何跳过Completablefuture 2和3?

1 个答案:

答案 0 :(得分:4)

您已经创建了这样的依赖关系链:

  first
    ↓  (↘)
   next  result
    ↓  ↙
  final

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

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

链接问题的答案会创建像

这样的阶段
    first
   (↙)  (↘)
 next 1  result
   ↓     |
 next 2  |
   ↘    ↙
    final

关键是,初始阶段将明确地完成两个CompletableFuture中的任何一个,而不是触发自动完成。另一个依赖阶段链永远不会被评估。由于final阶段是applyToEither,因此只有一个链的完成足以评估最终函数。它仍然不会影响前提阶段。

请注意,对于由thenCompose操作组成的链,您可以以更简单的方式实现类似的逻辑,因为您的函数无论如何都会返回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