You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

测试含CompletableFuture.allOf()的方法:allOf还是anyOf?

问题描述

我正在测试一个使用CompletableFuture.allOf()的方法,方法实现如下:

static CompletableFuture<byte[]> anySuccess(List<CompletableFuture<byte[]>> futures) {
    CompletableFuture<byte[]> delegateFuture = new CompletableFuture<>();

    CompletableFuture.allOf(futures.stream().map(s -> s.thenApply(t -> {
                if (t == null) {
                    // 这里把null视为查找失败
                    // 内部的虚拟异常用来给调用者传递返回null的信号
                    throw new RuntimeException();
                }
                return t;
            }).thenAccept(delegateFuture::complete)).toArray(CompletableFuture<?>[]::new))
            .exceptionally(ex -> {
                delegateFuture.completeExceptionally(ex);
                return null;
            });
    return delegateFuture;
}

输入的CompletableFuture集合会在不同时间完成,我的单元测试代码如下:

@Test
void testAnySuccess() {
    final byte[] payload1 = "payload1".getBytes();
    final byte[] payload2 = "payload2".getBytes();

    List<CompletableFuture<byte[]>> futures = Arrays.asList(
            CompletableFuture.supplyAsync(
                    () -> null
            ),
            CompletableFuture.supplyAsync(
                () -> {
                    LockSupport.parkNanos(Duration.ofSeconds(5).toNanos());
                    System.out.println("Wait for 5 seconds");
                    return payload2;
                }),
            CompletableFuture.supplyAsync(
                    () -> {
                        LockSupport.parkNanos(Duration.ofMillis(100).toNanos());
                        System.out.println("Wait for 100ms");
                        return payload1;
                    })
    );
    long start = System.nanoTime();
    CompletableFuture<byte[]> res = anySuccess(futures);
    byte[] actualRes = res.join();
    Assert.assertEquals(actualRes, payload1);
}

我清楚CompletableFuture.allOf()是非阻塞的,它返回的CompletableFuture要等所有子Future完成后才会完成。我的核心需求是:返回第一个非null值,如果所有值都是null则抛出异常。现在有两个疑问:

  1. 这种场景下应该用allOf还是anyOf?
  2. 是否需要修改代码,去掉exceptionally()改用thenAccept()?

解答

1. 优先选anyOf,而非allOf

你的需求是获取第一个成功返回非null的结果,allOf的特性是必须等待所有子Future完成才会结束,完全和需求相悖——用它的话你得等最慢的那个Future执行完,完全浪费了“取第一个有效结果”的优势。

anyOf才是匹配需求的选择:它会在任意一个子Future完成时就触发回调,正好能帮你实现“拿到第一个非null结果就返回”的逻辑。不过要注意,anyOf返回的是CompletableFuture<Object>,需要做类型转换,同时要处理其中可能的null或异常情况。

2. 代码修改建议

原代码存在两个核心问题:

  • 依赖allOf导致无法提前返回第一个有效结果,必须等全部Future完成
  • 把null转为RuntimeException的逻辑没必要,直接判断结果是否为null更直观

下面是优化后的实现:

static CompletableFuture<byte[]> anySuccess(List<CompletableFuture<byte[]>> futures) {
    CompletableFuture<byte[]> resultFuture = new CompletableFuture<>();

    // 给每个Future绑定回调:一旦返回非null结果,立刻完成resultFuture
    for (CompletableFuture<byte[]> future : futures) {
        future.thenAccept(result -> {
            if (result != null) {
                resultFuture.complete(result);
            }
        }).exceptionally(ex -> {
            // 单个Future的异常直接忽略,我们只关心有效结果
            return null;
        });
    }

    // 监听所有Future完成事件:如果此时resultFuture未完成,说明全是null,抛出异常
    CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
            .thenRun(() -> {
                if (!resultFuture.isDone()) {
                    resultFuture.completeExceptionally(new RuntimeException("All futures returned null"));
                }
            });

    return resultFuture;
}

这个实现的逻辑更清晰:

  • 遍历每个Future,只要有一个返回非null值,立刻通过resultFuture返回结果,实现“第一个有效结果快速返回”
  • 用allOf兜底:当所有Future都完成但没有任何有效结果时,抛出异常满足需求
  • 去掉了原代码中不必要的异常转换,直接通过结果判断逻辑更简洁

另外,原代码中的exceptionally可以调整:因为我们只关心成功的非null结果,单个Future的异常可以直接忽略,只有当所有Future都没返回有效结果时才抛出异常。


内容的提问来源于stack exchange,提问作者chrisrhyno2003

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 16:19:59