考虑以下代码
public class TestCompletableFuture {
BiConsumer<Integer, Throwable> biConsumer = (x,y) -> {
System.out.println(x);
System.out.println(y);
};
public static void main(String args[]) {
TestCompletableFuture testF = new TestCompletableFuture();
testF.start();
}
public void start() {
Supplier<Integer> numberSupplier = new Supplier<Integer>() {
@Override
public Integer get() {
return SupplyNumbers.sendNumbers();
}
};
CompletableFuture<Integer> testFuture = CompletableFuture.supplyAsync(numberSupplier).whenComplete(biConsumer);
}
}
class SupplyNumbers {
public static Integer sendNumbers(){
return 25; // just for working sake its not correct.
}
}
以上的事情很好。但是sendNumbers
也可以在我的情况下抛出一个检查过的异常,例如:
class SupplyNumbers {
public static Integer sendNumbers() throws Exception {
return 25; // just for working sake its not correct.
}
}
现在我想在我的y
中以biConsumer
处理此异常。这将帮助我在单个函数(biConsumer
)中处理结果以及异常(如果有的话)。
有什么想法吗?我可以在这里或其他任何地方使用CompletableFuture.exceptionally(fn)
吗?
答案 0 :(得分:14)
当您想要处理已检查的异常时,使用标准功能接口的工厂方法没有用。当您将捕获异常的代码插入lambda表达式时,您遇到的问题是catch子句需要CompletableFuture
实例来设置异常,而工厂方法需要Supplier
,鸡蛋和鸡蛋。
您可以使用类的实例字段在创建后允许变异,但最终,生成的代码不是干净的,而且比基于Executor
的直接解决方案更复杂。 documentation of CompletableFuture
说:
- 使用
执行没有显式Executor参数的所有 async 方法ForkJoinPool.commonPool()
...
因此,您知道以下代码将直接处理已检查的异常时显示CompletableFuture.supplyAsync(Supplier)
的标准行为:
CompletableFuture<Integer> f=new CompletableFuture<>();
ForkJoinPool.commonPool().submit(()-> {
try { f.complete(SupplyNumbers.sendNumbers()); }
catch(Exception ex) { f.completeExceptionally(ex); }
});
文档还说:
...为了简化监控,调试和跟踪,所有生成的异步任务都是标记接口
CompletableFuture.AsynchronousCompletionTask
的实例。
如果您希望遵守此约定以使解决方案更像原始supplyAsync
方法,请将代码更改为:
CompletableFuture<Integer> f=new CompletableFuture<>();
ForkJoinPool.commonPool().submit(
(Runnable&CompletableFuture.AsynchronousCompletionTask)()-> {
try { f.complete(SupplyNumbers.sendNumbers()); }
catch(Exception ex) { f.completeExceptionally(ex); }
});
答案 1 :(得分:7)
您已在y
中捕获该异常。也许你没有看到它,因为main
在CompletableFuture有机会完成之前退出了?
下面的代码打印&#34; null&#34;和#34;您好&#34;正如所料:
public static void main(String args[]) throws InterruptedException {
TestCompletableFuture testF = new TestCompletableFuture();
testF.start();
Thread.sleep(1000); //wait for the CompletableFuture to complete
}
public static class TestCompletableFuture {
BiConsumer<Integer, Throwable> biConsumer = (x, y) -> {
System.out.println(x);
System.out.println(y);
};
public void start() {
CompletableFuture.supplyAsync(SupplyNumbers::sendNumbers)
.whenComplete(biConsumer);
}
}
static class SupplyNumbers {
public static Integer sendNumbers() {
throw new RuntimeException("Hello");
}
}
答案 2 :(得分:3)
我不太确定你想要实现的目标。如果您的供应商抛出异常,当您致电testFuture .get()
时,您会因供应商抛出的任何异常而导致java.util.concurrent.ExecutionException
,您可以通过getCause()
上的ExecutionException
进行检索}。
或者,正如您所提到的,您可以在exceptionally
中使用CompletableFuture
。这段代码:
public class TestCompletableFuture {
private static BiConsumer<Integer, Throwable> biConsumer = (x,y) -> {
System.out.println(x);
System.out.println(y);
};
public static void main(String args[]) throws Exception {
Supplier<Integer> numberSupplier = () -> {
throw new RuntimeException(); // or return integer
};
CompletableFuture<Integer> testFuture = CompletableFuture.supplyAsync(numberSupplier)
.whenComplete(biConsumer)
.exceptionally(exception -> 7);
System.out.println("result = " + testFuture.get());
}
}
打印此结果:
null
java.util.concurrent.CompletionException: java.lang.RuntimeException
result = 7
修改强>
如果您检查过例外,您只需添加一个try-catch。
原始代码:
Supplier<Integer> numberSupplier = new Supplier<Integer>() {
@Override
public Integer get() {
return SupplyNumbers.sendNumbers();
}
};
修改后的代码:
Supplier<Integer> numberSupplier = new Supplier<Integer>() {
@Override
public Integer get() {
try {
return SupplyNumbers.sendNumbers();
} catch (Excetpion e) {
throw new RuntimeExcetpion(e);
}
}
};
答案 3 :(得分:1)
也许您可以使用新的Object来包装整数和错误,如下所示:
public class Result {
private Integer integer;
private Exception exception;
// getter setter
}
然后:
public void start(){
Supplier<Result> numberSupplier = new Supplier<Result>() {
@Override
public Result get() {
Result r = new Result();
try {
r.setInteger(SupplyNumbers.sendNumbers());
} catch (Exception e){
r.setException(e);
}
return r;
}
};
CompletableFuture<Result> testFuture = CompletableFuture.supplyAsync(numberSupplier).whenComplete(biConsumer);
}
答案 4 :(得分:1)
使用CompletableFuture
时要考虑到completeExceptionally()
中的异常处理的另一点是,确切的异常将在handle()
和whenComplete()
中可用,但将其包装在CompletionException
中调用join()
或将其转发到任何下游阶段时。
应用于下游阶段的handle()
或exceptionally()
将因此看到CompletionException
而不是原始的RuntimeException
,并且必须查看其原因以找到原始的异常。 / p>
此外,由任何操作(包括supplyAsync()
)引发的任何CompletionException
都被包装在CompletionException
中,除非它已经是CompletionException
。
考虑到这一点,最好在安全方面发挥作用,并让异常处理程序解开CompletableFuture
。
如果您这样做,就再也没有必要在CompletionException
上设置确切的(检查的)异常了,将检查的异常直接包装在Supplier<Integer> numberSupplier = () -> {
try {
return SupplyNumbers.sendNumbers();
} catch (Exception e) {
throw new CompletionException(e);
}
};
中要简单得多:
simpleWrap()
为了将此方法与Holger's approach进行比较,我使用两种解决方案修改了您的代码(customWrap()
以上,public class TestCompletableFuture {
public static void main(String args[]) {
TestCompletableFuture testF = new TestCompletableFuture();
System.out.println("Simple wrap");
testF.handle(testF.simpleWrap());
System.out.println("Custom wrap");
testF.handle(testF.customWrap());
}
private void handle(CompletableFuture<Integer> future) {
future.whenComplete((x1, y) -> {
System.out.println("Before thenApply(): " + y);
});
future.thenApply(x -> x).whenComplete((x1, y) -> {
System.out.println("After thenApply(): " + y);
});
try {
future.join();
} catch (Exception e) {
System.out.println("Join threw " + e);
}
try {
future.get();
} catch (Exception e) {
System.out.println("Get threw " + e);
}
}
public CompletableFuture<Integer> simpleWrap() {
Supplier<Integer> numberSupplier = () -> {
try {
return SupplyNumbers.sendNumbers();
} catch (Exception e) {
throw new CompletionException(e);
}
};
return CompletableFuture.supplyAsync(numberSupplier);
}
public CompletableFuture<Integer> customWrap() {
CompletableFuture<Integer> f = new CompletableFuture<>();
ForkJoinPool.commonPool().submit(
(Runnable & CompletableFuture.AsynchronousCompletionTask) () -> {
try {
f.complete(SupplyNumbers.sendNumbers());
} catch (Exception ex) {
f.completeExceptionally(ex);
}
});
return f;
}
}
class SupplyNumbers {
public static Integer sendNumbers() throws Exception {
throw new Exception("test"); // just for working sake its not correct.
}
}
是Holger的代码):
Simple wrap
After thenApply(): java.util.concurrent.CompletionException: java.lang.Exception: test
Before thenApply(): java.util.concurrent.CompletionException: java.lang.Exception: test
Join threw java.util.concurrent.CompletionException: java.lang.Exception: test
Get threw java.util.concurrent.ExecutionException: java.lang.Exception: test
Custom wrap
After thenApply(): java.util.concurrent.CompletionException: java.lang.Exception: test
Before thenApply(): java.lang.Exception: test
Join threw java.util.concurrent.CompletionException: java.lang.Exception: test
Get threw java.util.concurrent.ExecutionException: java.lang.Exception: test
输出:
whenComplete()
您会注意到,唯一的区别是在thenApply()
情况下,customWrap()
在thenApply()
之前看到了原始异常。在get()
之后,在所有其他情况下,原始异常都被包装。
最令人惊讶的是,CompletionException
将在“简单包装”的情况下解开ExecutionException
,并将其替换为 <app-datepicker1 (date1)="catchDate1($event)"></app-datepicker1>
<app-datepicker2 (date2)="catchDate2($event)"></app-datepicker2>
。