英文:
Add a description to CompletableFuture orTimeout
问题
以下是翻译好的部分:
这是我目前在调用该方法的调用者试图执行`get()`时得到的全部内容:
java.util.concurrent.ExecutionException: java.util.concurrent.TimeoutException
at java.base/java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:396)
at java.base/java.util.concurrent.CompletableFuture.get(CompletableFuture.java:2073)
我需要添加更多信息 - 哪个操作已经超时,超时多长时间等等。换句话说,我需要在异常中添加一些消息,例如:“在15秒内发生了超时”,最好使用供应商(supplier)如下所示:
CompletableFuture<ResutType> getSomething()
{
return someFuture.orTimeout(15, SECONDS, () -> new TimeoutException("在15秒内发生了超时"));
}
英文:
What is the easy way to add some addition info into the timeout exception? Suppose I have a code like that:
CompletableFuture<ResutType> getSomething()
{
... return someFuture.orTimeout(15, SECONDS);
}
This is all I'm getting currently downstream later on when callers of the method trying to do get()
:
java.util.concurrent.ExecutionException: java.util.concurrent.TimeoutException
at java.base/java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:396)
at java.base/java.util.concurrent.CompletableFuture.get(CompletableFuture.java:2073)
I need to add more info - which operation has timed out, how long timeout was, etc.. In other words I need some message to the exception like "The Something timed-out in 15s", ideally use a supplier like:
CompletableFuture<ResutType> getSomething()
{
return someFuture.orTimeout(15, SECONDS, () -> new TimeoutException("The Something timed-out in 15s"));
}
答案1
得分: 2
你可以使用与 CompletableFuture
内部的超时处理相同的设施:
public static <T> CompletableFuture<T> customTimeOut(
CompletableFuture<T> cf, long time, TimeUnit unit,
Supplier<? extends Exception> s) {
CompletableFuture.delayedExecutor(time, unit, cf.defaultExecutor())
.execute(() -> { if(!cf.isDone()) cf.completeExceptionally(s.get()); });
return cf;
}
请注意,isDone()
检查并不是严格必要的,因为如果未来已经完成,completeExceptionally
不会执行任何操作。检查只是为了在不必要时防止潜在的昂贵异常构造。虽然未来在 isDone()
和 completeExceptionally
调用之间被完成的可能性仍然存在,但这时不会发生任何不良情况,因为正如前面所说,completeExceptionally
会执行正确的操作。
你可以像这样进行测试:
public static void main(String[] args) {
for(int timeOut = 1; timeOut < 5; timeOut += 2) {
System.out.println("time-out " + timeOut + " sec");
CompletableFuture<String> cf = customTimeOut(
CompletableFuture.supplyAsync(() -> {
LockSupport.parkNanos(2_000_000_000);
return "result";
}),
timeOut, TimeUnit.SECONDS,
() -> new TimeoutException("timeout with my custom message")
);
try {
System.out.println("ordinary result: " + cf.join());
}
catch(Exception ex) {
ex.printStackTrace(System.out);
}
}
}
英文:
You can use the same facility as the CompletableFuture
’s internal timeout handling:
public static <T> CompletableFuture<T> customTimeOut(
CompletableFuture<T> cf, long time, TimeUnit unit,
Supplier<? extends Exception> s) {
CompletableFuture.delayedExecutor(time, unit, cf.defaultExecutor())
.execute(() -> { if(!cf.isDone()) cf.completeExceptionally(s.get()); });
return cf;
}
Note that the isDone()
check is not strictly necessary, as completeExceptionally
does nothing if the future is already completed. The check is only there to prevent the potential expensive exception construction when not necessary. It’s still possible, but rather unlikely that the future gets completed right between the isDone()
and the completeExceptionally
call, but nothing bad will happen then, as said, because completeExceptionally
does the right thing anyway.
You can test it like
public static void main(String[] args) {
for(int timeOut = 1; timeOut < 5; timeOut += 2) {
System.out.println("time-out " + timeOut + " sec");
CompletableFuture<String> cf = customTimeOut(
CompletableFuture.supplyAsync(() -> {
LockSupport.parkNanos(2_000_000_000);
return "result";
}),
timeOut, TimeUnit.SECONDS,
() -> new TimeoutException("timeout with my custom message")
);
try {
System.out.println("ordinary result: " + cf.join());
}
catch(Exception ex) {
ex.printStackTrace(System.out);
}
}
}
time-out 1 sec
java.util.concurrent.CompletionException: java.util.concurrent.TimeoutException: timeout with my custom message
at java.base/java.util.concurrent.CompletableFuture.reportJoin(CompletableFuture.java:413)
at java.base/java.util.concurrent.CompletableFuture.join(CompletableFuture.java:2118)
at CF.main(CF.java:32)
Caused by: java.util.concurrent.TimeoutException: timeout with my custom message
at CF.lambda$2(CF.java:28)
at CF.lambda$0(CF.java:13)
at java.base/java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1395)
at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:373)
at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1182)
at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1655)
at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1622)
at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:165)
time-out 3 sec
ordinary result: result
答案2
得分: 1
你可以使用 exceptionallyCompose
(或者这个方法的任何异步变体)来实现这个目标:
public final class Example {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
sleep(3000);
return "DONE";
})
.orTimeout(1000, TimeUnit.MILLISECONDS)
.exceptionallyCompose(throwable -> {
if (throwable instanceof TimeoutException) {
var tex = new TimeoutException("The Something timed-out in 15s");
return CompletableFuture.failedFuture(tex);
}
return CompletableFuture.failedFuture(throwable);
});
sleep(2000);
future.get();
sleep(5000);
}
private static void sleep(long ms) {
try {
Thread.sleep(ms);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
如果你的代码因为 TimeoutException
而失败,你可以通过一个失败的 CompletableFuture
返回一个带有更多详细信息的 TimeoutException
(或任何其他可抛出对象)。否则,你可以简单地返回引发异常的前一个阶段之一引发的相同的可抛出对象。
例如,如果在 supplyAsync
中出现了某个 RuntimeException
,future.get()
仍然会引发一个 RuntimeException
(包装在 ExecutionException
中)。如果没有异常(即也没有超时),则可以正常地调用 get()
获取结果。
有了这个知识,你可以编写自己的小型 orTimeout
包装器,例如:
public static <T> CompletableFuture<T> orTimeout(
CompletableFuture<T> cf,
long timeout, TimeUnit unit
) {
return cf.orTimeout(timeout, unit).exceptionallyCompose(throwable -> {
if (throwable instanceof TimeoutException) {
final var msg = String.format(
"The Something timed-out in %d %s.",
timeout, unit
);
return CompletableFuture.failedFuture(new TimeoutException(msg));
}
return CompletableFuture.failedFuture(throwable);
});
}
然后像这样为所有 CompletableFuture
使用它:
CompletableFuture<String> future = orTimeout(CompletableFuture.supplyAsync(() -> {
sleep(10000);
return "DONE";
}), 1000, TimeUnit.MILLISECONDS);
英文:
You can use exceptionallyCompose
(or any of the async variants of this method) for that:
public final class Example {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
sleep(3000);
return "DONE";
})
.orTimeout(1000, TimeUnit.MILLISECONDS)
.exceptionallyCompose(throwable -> {
if (throwable instanceof TimeoutException) {
var tex = new TimeoutException("The Something timed-out in 15s");
return CompletableFuture.failedFuture(tex);
}
return CompletableFuture.failedFuture(throwable);
});
sleep(2000);
future.get();
sleep(5000);
}
private static void sleep(long ms) {
try {
Thread.sleep(ms);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
If your code fails due to a TimeoutException
, you can return a TimeoutException
(or any other throwable) that has more detail via a failed CompletableFuture
. Otherwise, you can just return the same throwable that caused one of the previous stages to be completed exceptionally.
For example, if there had been some RuntimeException
in supplyAsync
, future.get()
would still cause a RuntimeException
(wrapped within an ExecutionException
) to be thrown. If there is no exception (i.e. also no timeout), you get()
your result normally.
With that knowledge you can write your own little orTimeout
wrapper, for example:
public static <T> CompletableFuture<T> orTimeout(
CompletableFuture<T> cf,
long timeout, TimeUnit unit
) {
return cf.orTimeout(timeout, unit).exceptionallyCompose(throwable -> {
if (throwable instanceof TimeoutException) {
final var msg = String.format(
"The Something timed-out in %d %s.",
timeout, unit
);
return CompletableFuture.failedFuture(new TimeoutException(msg));
}
return CompletableFuture.failedFuture(throwable);
});
}
and then use it for all CompletableFuture
s like so:
CompletableFuture<String> future = orTimeout(CompletableFuture.supplyAsync(() -> {
sleep(10000);
return "DONE";
}), 1000, TimeUnit.MILLISECONDS);
答案3
得分: 0
Since you are expecting a timeout, consider making it part of the return type instead of dealing with the TimeoutException
. One way is to change the result type to Either<Timeout, Result>
using the Either
type of the Vavr library.
var future = CompletableFuture.<Either<Timeout, Result>>supplyAsync(() ->
Either.right(lengthyCalculation()))
.completeOnTimeout(
Either.left(new Timeout("Lengthy Calculation timed out", 12)), 1, SECONDS);
Once the future completed and you got hold of its value, use one of the many methods of Either
to proceed. In the simplest case:
Result result = future.get()
.getOrElseThrow(timeout -> new RuntimeException(...));
英文:
Since you are expecting a timeout, consider making it part of the return type instead of dealing with the TimeoutException
. One way is to change the result type to Either<Timeout, Result>
using the Either
type of the Vavr library.
var future = CompletableFuture.<Either<Timeout, Result>>supplyAsync(() ->
Either.right(lengthyCalculation()))
.completeOnTimeout(
Either.left(new Timeout("Lengthy Calculation timed out", 12)), 1, SECONDS);
Once the future completed and you got hold of its value, use one of the many methods of Either
to proceed. In the simplest case
Result result = future.get()
.getOrElseThrow(timeout -> new RuntimeException(...));
答案4
得分: 0
我会这样说。
CompletableFuture future = CompletableFuture.runAsync(() -> {
try {
someFuture.get(15, TimeUnit.MILLISECONDS);
} catch (Exception e) {
throw new RuntimeException("详细消息", e);
}
});
当然,你应该对异常进行更详细的说明,但我认为这是问题的要点。
英文:
Naively I would say.
CompletableFuture future = CompletableFuture.runAsync( () ->{
try{
someFuture.get(15, TimeUnit.MILLISECONDS);
} catch(Exception e){
throw new RuntimeException("detailed message", e);
}
});
Of course you should be a bit more specific about the exceptions, but I think this is the gist of the issue.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论