java - Chain CompletableFuture 并在第一次成功时停止

标签 java java-8 completable-future

我正在使用一个返回 CompletableFuture 的 API 来查询设备(类似于 digitalpetri modbus )。

我需要使用几个选项来调用此 API 来查询设备并找出它是什么 - 这基本上是反复试验,直到成功。这些是我无法更改的嵌入式设备协议(protocol),但您可以将该过程视为类似于以下内容的工作方式:

  1. 你是苹果吗?
  2. 如果不是,那你是菠萝吗?
  3. 如果不是,那你是一支笔吗?
  4. ...

虽然 API 使用 future,但实际上,通信是串行的(通过同一物理线路),因此它们永远不会同步执行。一旦我知道它是什么,我希望能够停止尝试并让调用者知道它是什么。

我已经知道我只能使用 any 获得其中一个 future 的结果(见下文),但这可能会导致应避免的额外尝试。

是否存在一种链接 future 的模式,一旦其中一个成功,您就停止?

类似,但浪费了非常有限的资源。

List<CompletableFuture<String>> futures = Arrays.asList(
    CompletableFuture.supplyAsync(() -> "attempt 1"),
    CompletableFuture.supplyAsync(() -> "attempt 2"),
    CompletableFuture.supplyAsync(() -> "attempt 3"));

CompletableFuture<String>[] futuresArray = (CompletableFuture<String>[]) futures.toArray();
CompletableFuture<Object> c = CompletableFuture.anyOf(futuresArray);

最佳答案

假设您有一个如您所描述的“伪异步”方法,即它有一个异步 API,但需要一些锁定才能执行:

private final static Object lock = new Object();

private static CompletableFuture<Boolean> pseudoAsyncCall(int input) {
    return CompletableFuture.supplyAsync(() -> {
                synchronized (lock) {
                    System.out.println("Executing for " + input);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    return input > 3;
                }
            });
}

还有一个List<Integer>您想要根据此方法检查的输入的数量,您可以使用递归组合按顺序检查每个输入:

public static CompletableFuture<Integer> findMatch(List<Integer> inputs) {
    return findMatch(inputs, 0);
}

private static CompletableFuture<Integer> findMatch(List<Integer> inputs, int startIndex) {
    if (startIndex >= inputs.size()) {
        // no match found -- an exception could be thrown here if preferred
        return CompletableFuture.completedFuture(null);
    }
    return pseudoAsyncCall(inputs.get(startIndex))
            .thenCompose(result -> {
                if (result) {
                    return CompletableFuture.completedFuture(inputs.get(startIndex));
                } else {
                    return findMatch(inputs, startIndex + 1);
                }
            });
}

这将像这样使用:

public static void main(String[] args) {
    List<Integer> inputs = Arrays.asList(0, 1, 2, 3, 4, 5);
    CompletableFuture<Integer> matching = findMatch(inputs);

    System.out.println("Found match: " + matching.join());
}

输出:

Executing for 0
Executing for 1
Executing for 2
Executing for 3
Executing for 4
Found match: 4

如您所见,没有调用它来输入 5 ,而您的 API ( findMatch() ) 保持异步。

关于java - Chain CompletableFuture 并在第一次成功时停止,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50517771/

相关文章:

java - 如何让 Selenium WebDriver (Java) 单击此按钮?

java - 如何正确找到Java-8中的流特征?

java - 禁用 Flowable/Observable 缓冲区

Java 8 使用 CompletableFuture::join 维护流顺序

Java 8 - 如何使用 CompletableFuture 跟踪异步并行流中调用的异常数量

java - ProcessBuilder和Process.waitFor(),它等了多久?

java - Cloud PubSub Java 并行致谢

java - java中有些私有(private)内部类可以不继承吗?

java - 为什么 list.sort 不使用 Optional API

java - 是否可以安排 CompletableFuture?