企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
### 异常 与 `CompletableFuture` 在处理链中包装对象的方式相同,它也会缓冲异常。这些在处理时调用者是无感的,但仅当你尝试提取结果时才会被告知。为了说明它们是如何工作的,我们首先创建一个类,它在特定的条件下抛出一个异常: ```java // concurrent/Breakable.java import java.util.concurrent.*; public class Breakable { String id; private int failcount; public Breakable(String id, int failcount) { this.id = id; this.failcount = failcount; } @Override public String toString() { return "Breakable_" + id + " [" + failcount + "]"; } public static Breakable work(Breakable b) { if (--b.failcount == 0) { System.out.println( "Throwing Exception for " + b.id + "" ); throw new RuntimeException( "Breakable_" + b.id + " failed" ); } System.out.println(b); return b; } } ``` 当`failcount` > 0,且每次将对象传递给 `work()` 方法时, `failcount - 1` 。当`failcount - 1 = 0` 时,`work()` 将抛出一个异常。如果传给 `work()` 的 `failcount = 0` ,`work()` 永远不会抛出异常。 注意,异常信息此示例中被抛出( `RuntimeException` ) 在下面示例 `test()` 方法中,`work()` 多次应用于 `Breakable`,因此如果 `failcount` 在范围内,就会抛出异常。然而,在测试`A`到`E`中,你可以从输出中看到抛出了异常,但它们从未出现: ```java // concurrent/CompletableExceptions.java import java.util.concurrent.*; public class CompletableExceptions { static CompletableFuture<Breakable> test(String id, int failcount) { return CompletableFuture.completedFuture( new Breakable(id, failcount)) .thenApply(Breakable::work) .thenApply(Breakable::work) .thenApply(Breakable::work) .thenApply(Breakable::work); } public static void main(String[] args) { // Exceptions don't appear ... test("A", 1); test("B", 2); test("C", 3); test("D", 4); test("E", 5); // ... until you try to fetch the value: try { test("F", 2).get(); // or join() } catch (Exception e) { System.out.println(e.getMessage()); } // Test for exceptions: System.out.println( test("G", 2).isCompletedExceptionally() ); // Counts as "done": System.out.println(test("H", 2).isDone()); // Force an exception: CompletableFuture<Integer> cfi = new CompletableFuture<>(); System.out.println("done? " + cfi.isDone()); cfi.completeExceptionally( new RuntimeException("forced")); try { cfi.get(); } catch (Exception e) { System.out.println(e.getMessage()); } } } ``` 输出结果: ``` Throwing Exception for A Breakable_B [1] Throwing Exception for B Breakable_C [2] Breakable_C [1] Throwing Exception for C Breakable_D [3] Breakable_D [2] Breakable_D [1] Throwing Exception for D Breakable_E [4] Breakable_E [3] Breakable_E [2] Breakable_E [1] Breakable_F [1] Throwing Exception for F java.lang.RuntimeException: Breakable_F failed Breakable_G [1] Throwing Exception for G true Breakable_H [1] Throwing Exception for H true done? false java.lang.RuntimeException: forced ``` 测试 `A` 到 `E` 运行到抛出异常,然后…并没有将抛出的异常暴露给调用方。只有在测试F中调用 `get()` 时,我们才会看到抛出的异常。 测试 `G` 表明,你可以首先检查在处理期间是否抛出异常,而不抛出该异常。然而,test `H` 告诉我们,不管异常是否成功,它仍然被视为已“完成”。 代码的最后一部分展示了如何将异常插入到 `CompletableFuture` 中,而不管是否存在任何失败。 在连接或获取结果时,我们使用 `CompletableFuture` 提供的更复杂的机制来自动响应异常,而不是使用粗糙的 `try-catch`。 你可以使用与我们看到的所有 `CompletableFuture` 相同的表单来完成此操作:在链中插入一个 `CompletableFuture` 调用。有三个选项 `exceptionally()`,`handle()`, `whenComplete()`: ```java // concurrent/CatchCompletableExceptions.java import java.util.concurrent.*; public class CatchCompletableExceptions { static void handleException(int failcount) { // Call the Function only if there's an // exception, must produce same type as came in: CompletableExceptions .test("exceptionally", failcount) .exceptionally((ex) -> { // Function if (ex == null) System.out.println("I don't get it yet"); return new Breakable(ex.getMessage(), 0); }) .thenAccept(str -> System.out.println("result: " + str)); // Create a new result (recover): CompletableExceptions .test("handle", failcount) .handle((result, fail) -> { // BiFunction if (fail != null) return "Failure recovery object"; else return result + " is good"; }) .thenAccept(str -> System.out.println("result: " + str)); // Do something but pass the same result through: CompletableExceptions .test("whenComplete", failcount) .whenComplete((result, fail) -> { // BiConsumer if (fail != null) System.out.println("It failed"); else System.out.println(result + " OK"); }) .thenAccept(r -> System.out.println("result: " + r)); } public static void main(String[] args) { System.out.println("**** Failure Mode ****"); handleException(2); System.out.println("**** Success Mode ****"); handleException(0); } } ``` 输出结果: ``` **** Failure Mode **** Breakable_exceptionally [1] Throwing Exception for exceptionally result: Breakable_java.lang.RuntimeException: Breakable_exceptionally failed [0] Breakable_handle [1] Throwing Exception for handle result: Failure recovery object Breakable_whenComplete [1] Throwing Exception for whenComplete It failed **** Success Mode **** Breakable_exceptionally [-1] Breakable_exceptionally [-2] Breakable_exceptionally [-3] Breakable_exceptionally [-4] result: Breakable_exceptionally [-4] Breakable_handle [-1] Breakable_handle [-2] Breakable_handle [-3] Breakable_handle [-4] result: Breakable_handle [-4] is good Breakable_whenComplete [-1] Breakable_whenComplete [-2] Breakable_whenComplete [-3] Breakable_whenComplete [-4] Breakable_whenComplete [-4] OK result: Breakable_whenComplete [-4] ``` - `exceptionally()` 参数仅在出现异常时才运行。`exceptionally()` 局限性在于,该函数只能返回输入类型相同的值。 - `exceptionally()` 通过将一个好的对象插入到流中来恢复到一个可行的状态。 - `handle()` 一致被调用来查看是否发生异常(必须检查fail是否为true)。 - 但是 `handle()` 可以生成任何新类型,所以它允许执行处理,而不是像使用 `exceptionally()`那样简单地恢复。 - `whenComplete()` 类似于handle(),同样必须测试它是否失败,但是参数是一个消费者,并且不修改传递给它的结果对象。