是否有一种方法可以尝试等待CompletableFuture
一定时间后再返回不同的结果无在超时后取消将来?
我有一项服务(我们称其为expensiveService
),该服务会自行运行。返回结果:
enum Result {
COMPLETED,
PROCESSING,
FAILED
}
我愿意[阻止并等待]一小段时间(比如说2 s)。如果还没有完成,我想返回一个不同的结果,但是我希望服务继续做自己的事情。然后查询服务是否完成(例如通过websockets或其他方法)是客户的工作。
即我们有以下几种情况:
expensiveService.processAndGet()
需要1 s并完成其未来。返回COMPLETED
。expensiveService.processAndGet()
在1秒钟后失败。返回FAILED
。expensiveService.processAndGet()
需要5秒钟并完成其未来。返回PROCESSING
。如果我们要求其他服务提供结果,则会得到COMPLETED
。expensiveService.processAndGet()
在5秒钟后失败。返回PROCESSING
。如果我们要求其他服务提供结果,则会得到FAILED
。在这种特定情况下,实际上我们无论如何都需要在超时时获取当前结果对象,从而导致以下额外的边缘情况。这会导致以下建议的解决方案出现一些问题:
expensiveService.processAndGet()
耗时2.01秒,并完成了未来。它返回PROCESSING
或COMPLETED
。我也在使用Vavr,并愿意使用Vavr的Future
提出建议。
我们创建了三种可能的解决方案,它们都有各自的积极和消极:
CompletableFuture<Result> f = expensiveService.processAndGet();
return f.applyToEither(Future.of(() -> {
Thread.sleep(2000);
return null;
}).map(v -> resultService.get(processId)).toCompletableFuture(),
Function.identity());
resultService
。CompletableFuture<Result> f = expensiveService.processAndGet();
return f.applyToEither(Future.of(() -> {
int attempts = 0;
int timeout = 20;
while (!f.isDone() && attempts * timeout < 2000) {
Thread.sleep(timeout);
attempts++;
}
return null;
}).map(v -> resultService.get(processId)).toCompletableFuture(),
Function.identity());
resultService
仍然总是被调用。Object.notify
Object monitor = new Object();
CompletableFuture<Upload> process = expensiveService.processAndGet();
synchronized (monitor) {
process.whenComplete((r, e) -> {
synchronized (monitor) {
monitor.notifyAll();
}
});
try {
int attempts = 0;
int timeout = 20;
while (!process.isDone() && attempts * timeout < 2000) {
monitor.wait(timeout);
attempts++;
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
if (process.isDone()) {
return process.toCompletableFuture();
} else {
return CompletableFuture.completedFuture(resultService.get(processId));
}
Future.await
return Future.of(() -> expensiveService.processAndGet() .await(2, TimeUnit.SECONDS) .recoverWith(e -> { if (e instanceof TimeoutException) { return Future.successful(resultService.get(processId)); } else { return Future.failed(e); } }) .toCompletableFuture();
问题
await
取消内部将来。ThreadLocal
的[旧版]代码。recoverWith
和捕捉TimeoutException
不太好。CompletableFuture.orTimeout
return expensiveService.processAndGet() .orTimeout(2, TimeUnit.SECONDS) .<CompletableFuture<Upload>>handle((u, e) -> { if (u != null) { return CompletableFuture.completedFuture(u); } else if (e instanceof TimeoutException) { return CompletableFuture.completedFuture(resultService.get(processId)); } else { return CompletableFuture.failedFuture(e); } }) .thenCompose(Function.identity());
问题
processAndGet
的未来并没有被取消,但它应该是。CompletableFuture.completeOnTimeout
return expensiveService.processAndGet() .completeOnTimeout(null, 2, TimeUnit.SECONDS) .thenApply(u -> { if (u == null) { return resultService.get(processId); } else { return u; } });
问题
processAndGet
的未来还没有完成,但是根据文档,它应该是。processAndGet
要返回null
作为其他状态怎么办?所有这些解决方案都有缺点,并且需要额外的代码,但是感觉像CompletableFuture
或Vavr的Future
都应立即提供支持。有更好的方法吗?
有没有一种方法可以等待CompletableFuture一定的时间,然后再给出不同的结果,而又不会在超时后取消将来的结果?我有一项服务(我们称它为...
首先需要指出CompletableFuture
的工作方式(或为什么这样命名):