您可以使用以下辅助方法:
public static <T>
CompletableFuture<T> anyOf(List<? extends CompletionStage<? extends T>> l) {
CompletableFuture<T> f=new CompletableFuture<>();
Consumer<T> complete=f::complete;
l.forEach(s -> s.thenAccept(complete));
return f;
}
您可以像这样使用它,以证明它将忽略早期的异常但返回第一个提供的值:
List<CompletableFuture<String>> futures = Arrays.asList(
CompletableFuture.supplyAsync(
() -> { throw new RuntimeException("failing immediately"); }
),
CompletableFuture.supplyAsync(
() -> { LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(5));
return "with 5s delay";
}),
CompletableFuture.supplyAsync(
() -> { LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(10));
return "with 10s delay";
})
);
CompletableFuture<String> c = anyOf(futures);
logger.info(c.join());
此解决方案的一个缺点是,如果 all 期货异常完成,它将永远完成。如果计算成功则提供第一个值但如果根本没有成功计算则异常失败的解决方案涉及更多:
public static <T>
CompletableFuture<T> anyOf(List<? extends CompletionStage<? extends T>> l) {
CompletableFuture<T> f=new CompletableFuture<>();
Consumer<T> complete=f::complete;
CompletableFuture.allOf(
l.stream().map(s -> s.thenAccept(complete)).toArray(CompletableFuture<?>[]::new)
).exceptionally(ex -> { f.completeExceptionally(ex); return null; });
return f;
}
它利用了 allOf 的异常处理程序仅在所有期货都完成后(无论是否例外)才被调用的事实,并且未来只能完成一次(抛开像 obtrude… 这样的特殊事物)。当异常处理程序被执行时,任何试图以结果完成未来的尝试都已经完成(如果有的话),因此只有在之前没有成功完成的情况下,尝试异常完成它才会成功。
它可以与第一个解决方案完全相同的方式使用,并且只有在所有计算失败时才会表现出不同的行为,例如:
List<CompletableFuture<String>> futures = Arrays.asList(
CompletableFuture.supplyAsync(
() -> { throw new RuntimeException("failing immediately"); }
),
CompletableFuture.supplyAsync(
// delayed to demonstrate that the solution will wait for all completions
// to ensure it doesn't miss a possible successful computation
() -> { LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(5));
throw new RuntimeException("failing later"); }
)
);
CompletableFuture<String> c = anyOf(futures);
try { logger.info(c.join()); }
catch(CompletionException ex) { logger.severe(ex.toString()); }
上面的示例使用延迟来演示解决方案将在没有成功时等待所有完成,而this example on ideone 将演示稍后的成功如何将结果变为成功。请注意,由于 Ideones 缓存结果,您可能不会注意到延迟。
请注意,如果所有期货都失败,则无法保证会报告哪些异常。由于它在错误情况下等待所有完成,因此任何人都可以到达最终结果。