我有一系列异步服务调用,我想取消。好吧,实际上,我有两个服务调用链并行,如果一个成功,我想取消另一个。
对于番石榴的未来,我习惯于通过取消最后一个来取消整个期货连锁店。看来我不能用java-8的未来做到这一点。 除非有人对如何做好。
您的任务,如果您选择接受,就是告诉我是否可以保留我的漂亮语法并取消链条。否则,我将编写自己的链接未来包装器 - 特别是在this question之后。
我自己的测试和尝试随之而来。
@Test
public void shouldCancelOtherFutures() {
// guava
ListenableFuture<String> as = Futures.immediateFuture("a");
ListenableFuture<String> bs = Futures.transform(as, (AsyncFunction<String, String>) x -> SettableFuture.create());
ListenableFuture<String> cs = Futures.transform(bs, (AsyncFunction<String, String>) x -> SettableFuture.create());
ListenableFuture<String> ds = Futures.transform(cs, Functions.<String>identity());
ds.cancel(false);
assertTrue(cs.isDone()); // succeeds
// jdk 8
CompletableFuture<String> ac = CompletableFuture.completedFuture("a");
CompletableFuture<String> bc = ac.thenCompose(x -> new CompletableFuture<>());
CompletableFuture<String> cc = bc.thenCompose(x -> new CompletableFuture<>());
CompletableFuture<String> dc = cc.thenApply(Function.identity());
dc.cancel(false);
assertTrue(cc.isDone()); // fails
}
(想象一下,每个thenCompose()
和Futures.transform(x, AsyncFunction)
代表异步服务调用。)
我可以看到为什么道格李的研究生军队这样做了。有了分支链,是否应取消所有内容?
CompletableFuture<Z> top = new CompletableFuture<>()
.thenApply(x -> y(x))
.thenCompose(y -> z(y));
CompletableFuture<?> aBranch = top.thenCompose(z -> aa(z));
CompletableFuture<?> bBranch = top.thenCompose(z -> bb(z));
...
bBranch.cancel(false);
// should aBranch be canceled now?
我可以使用自定义包装器函数来解决这个问题,但它会混淆漂亮的语法。
private <T,U> CompletableFuture<U> transformAsync(CompletableFuture<T> source, Function<? super T,? extends CompletableFuture<U>> transform) {
CompletableFuture<U> next = source.thenCompose(transform);
next.whenComplete((x, err) -> next.cancel(false));
return next;
}
private <T,U> CompletableFuture<U> transform(CompletableFuture<T> source, Function<T,U> transform) {
CompletableFuture<U> next = source.thenApply(transform);
next.whenComplete((x, err) -> next.cancel(false));
return next;
}
// nice syntax I wished worked
CompletableFuture<?> f1 = serviceCall()
.thenApply(w -> x(w))
.thenCompose(x -> serviceCall())
.thenCompose(y -> serviceCall())
.thenApply(z -> $(z));
// what works, with less readable syntax
CompletableFuture<?> f2 =
transform(
transformAsync(
transformAsync(
transform(serviceCall, x(w)),
x -> serviceCall()),
y -> serviceCall()),
z -> $(z));
答案 0 :(得分:8)
这取决于你的目标是什么。我假设,让您的中间CompletableFuture
报告完成状态并不重要,因为在使用链式构造调用时通常不会注意到这一点。重要的是,您希望不要触发昂贵的serviceCall()
。
一种解决方案可以是:
CompletableFuture<String> flag=new CompletableFuture<>();
CompletableFuture<String> ac = serviceCall()
.thenCompose(x -> flag.isCancelled()? flag: serviceCall())
.thenCompose(x -> flag.isCancelled()? flag: serviceCall());
ac.whenComplete((v,t)->flag.cancel(false));// don’t chain this call
这会使用您的解决方案中的whenComplete
调用,但仅限于最终的CompletableFuture
,以便将取消传播到专用的flag
对象。调用cancel
后,下一个thenCompose
调用将检测取消并返回已取消的未来,因此取消将传播链,然后不再调用compose或apply方法。
缺点是这不能与thenApply
结合使用,因为Function
无法返回已取消的未来。因此,当异步服务调用完成并与Function
链接时,即使发生取消,也将应用该函数。
解决此问题的替代解决方案是为serviceCall
创建一个包装函数,其中包括启动前和完成后的测试:
CompletableFuture<String> serviceCall(CompletableFuture<String> f) {
if(f.isCancelled()) return f;
CompletableFuture<String> serviceCall=serviceCall();
return serviceCall.thenCompose(x->f.isCancelled()? f: serviceCall);
}
然后您的用例将如下所示:
CompletableFuture<String> flag=new CompletableFuture<>();
CompletableFuture<String> ac = serviceCall(flag)
.thenApply(w->x(w))
.thenCompose(x -> serviceCall(flag))
.thenCompose(x -> serviceCall(flag))
.thenApply(z -> $(z));
ac.whenComplete((v,t)->flag.cancel(false));
当然,您必须使用原始<String>
用于serviceCall()
的任何类型参数替换CompletableFuture<T>
。