我需要(根据计算结果)动态组合CompletionStages
以便不阻止执行,最终我需要捕获操作期间可能出现的异常,以便仔细关闭执行。
我已经实现了以下类似内容:
public CompletableFuture<Data> getData() {
final Data accumulator = new Data();
CompletableFuture<Data> result = new CompletableFuture<>();
CompletableStage exec = ... //starting execution
exec.thenComposeAsync(
(res) -> process(accumulator, res)
).thenAccept(t -> result.complete(accumulator));
return result;
}
private CompletionStage<Void> process(Data acc, Result res) {
res.data().forEach(
currData -> {
add.addData(currData);
}
);
if (res.hasMoreData()) {
return res.fetchNextData().thenComposeAsync(
(nextData) -> process(acc, nextData)
);
}
return CompletableFuture.completedFuture(null);
}
我不知道这是否是实现该解决方案的最佳方法,但如果一切正常,它会起作用。当由于某种原因在forEach
块中出现异常时,问题就来了,该错误不会传播回getData
调用者,因此我无法使用exceptionally
方法捕获该错误以停止我的以安全的方式进行应用。我想我做错了。
答案 0 :(得分:2)
如果传递给thenComposeAsync
的函数因异常而失败,则thenComposeAsync
返回的未来将异常完成。这导致由链式普通业务创建的期货也异常完成,而没有评估其功能。
该规则有三个例外,exceptionally
仅在异常完成后才进行评估,以产生替换值,而handle
和whenComplete
在两种情况下均进行评估。
因此,当您要使用备用值替换异常时,可以使用
exec.thenComposeAsync(res -> process(accumulator, res))
.exceptionally(throwable -> fallBack)
.thenAccept(t -> result.complete(accumulator));
在exceptionally
之前必须特别注意将链thenAccept
链接起来,否则在特殊情况下传递给thenAccept
的函数将不会得到评估。
当您要将异常传播到result
将来时,可以使用
exec.thenComposeAsync(res -> process(accumulator, res))
.whenComplete((value, throwable) -> {
if(throwable == null) result.complete(accumulator);
else result.completeExceptionally(throwable);
});
至关重要的是,将throwable
与null
进行比较,以确定完成是否出色,因为value
可以作为null
的普通结果。即使在普通结果值永远不能为null
的情况下,也建议您遵循惯用的解决方案,因为您不知道是否以及何时重用代码。