如何从 CompletableFuture 抛出自定义异常?

mem*_*und 5 java java-stream completable-future

问题:如何直接从 抛出自定义异常.exceptionally()

List<CompletableFuture<Object>> futures =
    tasks.stream()
        .map(task -> CompletableFuture.supplyAsync(() -> businessLogic(task))
        .exceptionally(ex -> {
                if (ex instanceof BusinessException) return null;

                //TODO how to throw a custom exception here??
                throw new BadRequestException("at least one async task had an exception");
        }))
        .collect(Collectors.toList());

try {
    List<Object> results = futures.stream()
        .map(CompletableFuture::join)
        .collect(Collectors.toList());
} catch (CompletionException e) {
        if (e.getCause() instanceof RuntimeException) {
              throw (RuntimeException) e.getCause();
        }
        throw new RuntimeException(e.getCause());
}
Run Code Online (Sandbox Code Playgroud)

问题:我总是得到一个CompletionExceptionwho ex.getCause()is instanceof BadRequestException

这可能吗?

Hol*_*ger 6

正如Didier L 所说,函数抛出的异常(或者通常是完成a 的异常CompletableFuture)总是包含在 a 中CompletionException(除非它们已经是 aCompletionExceptionCancellationException)。

但请注意,即使不尝试通过exceptionally以下方式翻译异常,您的代码也会变得更加简单:

List<CompletableFuture<Object>> futures =
    tasks.stream()
        .map(task -> CompletableFuture.supplyAsync(() -> businessLogic(task)))
        .collect(Collectors.toList());
try {
    List<Object> results = futures.stream()
        .map(CompletableFuture::join)
        .collect(Collectors.toList());
} catch (CompletionException e) {
    throw e.getCause() instanceof BusinessException?
        new BadRequestException("at least one async task had an exception"): e;
}
Run Code Online (Sandbox Code Playgroud)

或者

… catch (CompletionException e) {
    throw e.getCause() instanceof BusinessException?
        new BadRequestException("at least one async task had an exception"):
        e.getCause() instanceof BusinessException? (RuntimeException)e.getCause(): e;
}
Run Code Online (Sandbox Code Playgroud)

由于exceptionally的主要目的是将异常转换为非异常结果值,因此使用它来将异常转换为另一个抛出的异常并不是最合适的,它也需要一个instanceof. 因此,在catch子句中执行此翻译可以避免另一个翻译步骤。

  • 但是,通过包装 `businessLogic` 就可以很容易地解决这个问题,而不需要在其他地方做任何努力: `CompletableFuture.supplyAsync(() -&gt; { try { returnbusinessLogic(task); } catch(BusinessException ex) { return null; } } )`...`CompletableFuture` 不可能让这件事变得更简单。 (2认同)
  • 这很棘手,因为传递给“exceptionally”的函数接收一个“Throwable”,但只允许抛出未经检查的异常,因此重新抛出是受到限制的。正如答案中所说,“例外”的目的是(总是)用后备值替换异常,而不是重新抛出。在此基础上构建重新投掷功能将非常复杂。另请参阅[此答案](/sf/answers/3108801531/)。 (2认同)