从异步方法的同步部分抛出异常,返回CompletableFuture

时间:2018-08-16 18:43:55

标签: java asynchronous exception-handling completable-future

此问题仅适用于Java和CompletableFuture

如果我有以下异步方法,

CompletableFuture<String> get() {
    /* Step #1: Do some sync prep work */
    String s = doSomethingSync();

    /* Step #2: Do something async (returning CompletableFuture) */
    return doSomethingAsync(s);
}

如果抛出步骤#1中的代码,则get()的调用者将在获得返回的CompletableFuture之前得到一个异常,而如果抛出了步骤#2中返回的CompletableFuture内部的代码,呼叫者只有在与返回的CompletableFuture进行交互时才会获得异常。

这表明get()的调用者应该编写一些复杂的异常处理代码来处理这两种情况。

下面是另一个异步方法invokeGet()的示例,该方法调用get()并返回返回的String的长度:

CompletableFutre<Integer> InvokeGet() {
    try {
        CompletableFuture future = get();
        return furure.handle((result, throwable) -> {
           if (throwable != null) {
               /* Handle exception thrown in step #2 in get(), e.g. rethrow */
           } else {
               return result.length();
           }
        });
    } catch (Exception e) {
        /* Handle exception thrown in step #1 in get() */
        /* Return some value or throw */
    }
}

我的问题是:

get()是否写得不好,是因为它要求其调用者执行这种复杂的异常处理,或者这是一种通常且常见的模式?返回错误的CompletableFuture异步方法是否应该将自身限制为在出现错误的情况下返回有故障的期货,从而使调用者不必编写此类错误处理代码?

2 个答案:

答案 0 :(得分:0)

简而言之,它取决于您自己的实现,但可以进行改进。在您希望通过其他线程忽略,记录异常或对异常做出反应的情况下,让调用线程处理异常可能是有益的(基本示例here)。但是,我见过很多 patterns (见this posts answer),如果您将async函数包含在try-catch-block中,然后重新抛出更多的对您的应用程序有用异常,由父线程处理,我认为这要好一些。

如果您正在寻找不同的异常处理,请参见article,以获取不同处理的示例。

答案 1 :(得分:0)

我认为抛出异常以进行参数验证是适当的,因为调用者不应该处理这些异常–非法参数是必须修复的错误。

但是,最好将其他异常放入返回的CompletableFuture中,以便调用者可以使用标准的CompletableFuture异常处理和链接。同样,您将不会返回null的未来,而是会返回一个null完成的未来。另请参见CompletableFuture already completed with an exception