从CompletableFuture抛出异常

ayu*_*hgp 25 java exception-handling exception java-8 completable-future

我有以下代码:

// How to throw the ServerException?
public void myFunc() throws ServerException{
    // Some code
    CompletableFuture<A> a = CompletableFuture.supplyAsync(() -> {
        try {
            return someObj.someFunc();
        } catch(ServerException ex) {
            // throw ex; gives an error here.
        }
    }));
    // Some code
}
Run Code Online (Sandbox Code Playgroud)

someFunc()抛出一个ServerException.我不想在这里处理这个问题,而是将异常抛给someFunc()调用者myFunc().

Hol*_*ger 43

您的代码建议您稍后在同一方法中使用异步操作的结果,因此您CompletionException无论如何都必须处理,因此处理它的一种方法是

public void myFunc() throws ServerException {
    // Some code
    CompletableFuture<A> a = CompletableFuture.supplyAsync(() -> {
        try { return someObj.someFunc(); }
        catch(ServerException ex) { throw new CompletionException(ex); }
    });
    // Some code running in parallel to someFunc()

    A resultOfA;
    try {
        resultOfA = a.join();
    }
    catch(CompletionException ex) {
        try {
            throw ex.getCause();
        }
        catch(Error|RuntimeException|ServerException possible) {
            throw possible;
        }
        catch(Throwable impossible) {
            throw new AssertionError(impossible);
        }
    }
    // some code using resultOfA
}
Run Code Online (Sandbox Code Playgroud)

在异步处理中抛出的所有异常SupplierCompletionException在调用时被包装到一个join,除了ServerException我们已经包装在一个CompletionException.

当我们重新抛出原因时CompletionException,我们可能会遇到未经检查的异常,即Erroror的子类RuntimeException,或者我们的自定义检查异常ServerException.上面的代码使用multi-catch处理所有这些代码,这将重新抛出它们.由于声明的返回类型getCause()Throwable,编译器要求我们处理该类型,尽管我们已经处理了所有可能的类型.直截了当的解决办法就是扔掉这个实际上不可能的扔掉的东西AssertionError.

或者,我们可以为我们的自定义异常使用替代结果:

public void myFunc() throws ServerException {
    // Some code
    CompletableFuture<ServerException> exception = new CompletableFuture<>();
    CompletableFuture<A> a = CompletableFuture.supplyAsync(() -> {
        try { return someObj.someFunc(); }
        catch(ServerException ex) {
            exception.complete(ex);
            throw new CompletionException(ex);
        }
    });
    // Some code running in parallel to someFunc()

    A resultOfA;
    try {
        resultOfA = a.join();
    }
    catch(CompletionException ex) {
        if(exception.isDone()) throw exception.join();
        throw ex;
    }

    // some code using resultOfA
}
Run Code Online (Sandbox Code Playgroud)

此解决方案将以包装形式重新抛出所有"意外"抛出的抛出物,但只能将自定义ServerException以其原始形式投放到exception未来.请注意,在我们查询未来之前,我们必须确保a已完成(如join()先调用)exception,以避免竞争条件.

  • @Miguel `get` 与 `join` 的不同之处在于将异常包装在 `ExecutionException` 而不是 `CompletionException` 中。这对“catch”方面没有任何改进。它还要求调用者处理“InterruptedException”,这使得它更加复杂。此外,由于“Supplier”无法抛出已检查的“ExecutionException”,因此它必须保留“CompletionException”,然后“get”将提取原因并将其重新包装在“ExecutionException”中,这会降低效率。对于特殊情况来说,性能并不重要。但这里的“get”在各个方面都比“join”差。 (3认同)
  • 非常详细的回答。 (2认同)

mel*_*ngs 23

对于那些寻求其他通过completableFuture处理异常的方法的人

以下是处理解析错误到整数的示例的几种方法:

1. using handle方法 -使您可以提供异常的默认值

CompletableFuture correctHandler = CompletableFuture.supplyAsync(() -> "A")
            .thenApply(Integer::parseInt)
            .handle((result, ex) -> {
                if (null != ex) {
                    ex.printStackTrace();
                    return 0;
                } else {
                    System.out.println("HANDLING " + result);
                    return result;
                }
            })
            .thenAcceptAsync(s -> {
                System.out.println("CORRECT: " + s);
            });
Run Code Online (Sandbox Code Playgroud)

2.使用exceptionally方法 -相似handle但较不冗长

CompletableFuture parser = CompletableFuture.supplyAsync(() -> "1")
                .thenApply(Integer::parseInt)
                .exceptionally(t -> {
                    t.printStackTrace();
                    return 0;
                }).thenAcceptAsync(s -> System.out.println("CORRECT value: " + s));
Run Code Online (Sandbox Code Playgroud)

3.使用whenComplete方法 -使用此方法将停止该方法的执行,而不会执行下一个thenAcceptAsync

CompletableFuture correctHandler2 = CompletableFuture.supplyAsync(() -> "A")
                .thenApply(Integer::parseInt)
                .whenComplete((result, ex) -> {
                    if (null != ex) {
                        ex.printStackTrace();
                    }
                })
                .thenAcceptAsync(s -> {
                    System.out.println("When Complete: " + s);
                });
Run Code Online (Sandbox Code Playgroud)

4.通过传播异常 completeExceptionally

public static CompletableFuture<Integer> converter(String convertMe) {
        CompletableFuture<Integer> future = new CompletableFuture<>();
        try {
            future.complete(Integer.parseInt(convertMe));
        } catch (Exception ex) {
            future.completeExceptionally(ex);
        }
        return future;
    }
Run Code Online (Sandbox Code Playgroud)

  • 对于像我这样的人,由于`CompletableFuture &lt;Void&gt;`而无法使用1、2和3的情况,只需返回一个`null'即可,其中的对象将是`Void`类型的:)摆脱这个简单的事情。 (3认同)