从CompletableFuture抛出异常

时间:2017-06-07 10:23:30

标签: java exception exception-handling 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
}

someFunc()会抛出ServerException。我不想在这里处理此问题,而是将someFunc()的异常抛给myFunc()的调用者。

4 个答案:

答案 0 :(得分:29)

您的代码建议您稍后在同一方法中使用异步操作的结果,因此您无论如何都必须处理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
}

Supplier的异步处理中抛出的所有异常都会在调用CompletionException时被包含在join中,除了ServerException我们已经包含在CompletionException中1}}。

当我们重新抛出CompletionException的原因时,我们可能会遇到未经检查的异常,即ErrorRuntimeException的子类,或我们的自定义检查异常ServerException。上面的代码使用multi-catch处理所有这些代码,这将重新抛出它们。由于声明的返回类型getCause()Throwable,编译器要求我们处理该类型,尽管我们已经处理了所有可能的类型。直接的解决方案是将这个实际上不可能的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
}

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

答案 1 :(得分:11)

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

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

1。使用File(...)方法-通过该方法,您可以提供有关异常的默认值

FileRepositoryBuilder builder = new FileRepositoryBuilder();
String gitPath = "\path\to\your\git\folder";
Repository repository = builder.setGitDir(new File(gitPath))
    .readEnvironment().findGitDir().build();
listRepositoryContents(repository);
repository.close();

2。使用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); }); 类似,但较不详细

exceptionally

3。使用handle方法-使用此方法将停止该方法的执行,而不会执行下一个CompletableFuture parser = CompletableFuture.supplyAsync(() -> "1") .thenApply(Integer::parseInt) .exceptionally(t -> { t.printStackTrace(); return 0; }).thenAcceptAsync(s -> System.out.println("CORRECT value: " + s));

whenComplete

4。通过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);
                });

答案 2 :(得分:3)

我认为你应该把它包装成RuntimeException然后抛出:

 throw new RuntimeException(ex);

或许是一个小实用程序会有所帮助:

static class Wrapper extends RuntimeException {

    private Wrapper(Throwable throwable) {
        super(throwable);
    }

    public static Wrapper wrap(Throwable throwable) {
        return new Wrapper(throwable);
    }

    public Throwable unwrap() {
        return getCause();
    }
}


 public static void go() {
    CompletableFuture<String> a = CompletableFuture.supplyAsync(() -> {
        try {
            throw new Exception("Just because");
        } catch (Exception ex) {
            throw Wrapper.wrap(ex);
        }
    });

    a.join();
}

然后你可以unwrap ......

 try {
        go();
 } catch (Wrapper w) {
        throw w.unwrap();
 }

答案 3 :(得分:1)

即使其他人的答案非常好。但是我给你另一种方法在CompletableFuture中抛出一个检查过的异常。

IF 您不想在另一个帖子中调用CompletableFuture,您可以使用匿名类来处理它:

CompletableFuture<A> a = new CompletableFuture<A>() {{
    try {
        complete(someObj.someFunc());
    } catch (ServerException ex) {
        completeExceptionally(ex);
    }
}};

IF 你想在另一个线程中调用CompletableFuture,你也可以使用匿名类来处理它,但是按runAsync运行方法:

CompletableFuture<A> a = new CompletableFuture<A>() {{
    CompletableFuture.runAsync(() -> {
        try {
            complete(someObj.someFunc());
        } catch (ServerException ex) {
            completeExceptionally(ex);
        }
    });
}};