CompletableFuture withFallback /只处理一些错误

时间:2014-08-16 08:32:45

标签: java java-8 completable-future

我通过CompletableFuture收到服务电话的回复。我想处理服务返回的一些已知异常 - 例如乐观并发控制冲突。

这就是我所拥有的。有没有更好的方法来执行不包装异常或使用SneakyThrows?包装异常意味着其他异常处理程序必须检查因果链,而不是仅仅使用instanceof

someService.call(request)
    .handle((response, error) -> {
        if (error == null)
            return CompletableFuture.completedFuture(response);
        else if (error instanceof OCCException)
            return CompletableFuture.completedFuture(makeDefaultResponse());

        CompletableFuture<Response> errorFuture = new CompletableFuture<>();
        errorFuture.completeExceptionally(error);
        return errorFuture;
    }).thenCompose(Function.identity());

同样的,有没有办法复制番石榴withFallback而没有包装解开?

CompletableFuture<T> withFallback(CompletableFuture<T> future,
                                  Function<Throwable, ? extends CompletableFuture<T>> fallback) {
    return future.handle((response, error) -> {
        if (error == null)
            return CompletableFuture.completedFuture(response);
        else
            return fallback.apply(error);
    }).thenCompose(Function.identity());
}


...
// Here's the first part of the question implemented using withFallback.
// It's a little cleaner, but it still requires wrapping as a future.
withFallback(someService.call(request), error -> {
    if (error instanceof OCCException)
        return CompletableFuture.completedFuture(makeDefaultResponse());

    CompletableFuture<Response> errorFuture = new CompletableFuture<>();
    errorFuture.completeExceptionally(error);
    return errorFuture;
});

为了完整性,如果我允许要包装的异常,这就是它的样子。 (我有一个单元测试来验证抛出的异常在链中传播):

someService.call(request)
    .exceptionally(error -> {
        if (error instanceof OCCException)
            return makeDefaultResponse();
        else
            // wrap because error is declared as a checked exception
            throw new RuntimeException(error);
    });

2 个答案:

答案 0 :(得分:11)

您要求的番石榴风格功能可以像这样实现:

static <T> CompletableFuture<T> withFallback(CompletableFuture<T> future,
                  Function<Throwable, ? extends CompletableFuture<T>> fallback) {
  return future.handle((response, error) -> error)
    .thenCompose(error -> error!=null? fallback.apply(error): future);
}

通过在我们不想进行任何转换的情况下重复使用源代码来实现更紧凑和更节省资源。但是为了让调用者有机会在不引入另一个辅助方法的情况下执行相同的操作,更改方法并使用BiFunction非常有用,它将源未来作为附加参数:

static <T> CompletableFuture<T> withFallback(CompletableFuture<T> future,
  BiFunction<CompletableFuture<T>, Throwable, ? extends CompletableFuture<T>>
                                                                      fallback) {
    return future.handle((response, error) -> error)
      .thenCompose(error -> error!=null? fallback.apply(future,error): future);
}

然后你可以像这样使用它:

withFallback(someService.call(request), (f,t) -> t instanceof OCCException?
                  CompletableFuture.completedFuture(makeDefaultResponse()): f)

答案 1 :(得分:5)

我能想到的唯一方法是定义一个这样的辅助方法:

static <T, E extends Throwable> CompletableFuture<T> betterHandle(CompletableFuture<T> source, Class<E> excClass, Supplier<T> sup) {
    CompletableFuture<T> result = new CompletableFuture<>();
    source.whenComplete( (t, ex) -> {
        if (ex == null) {
            result.complete(t);
        } else if (excClass.isInstance(ex)) {
            result.complete(sup.get());
        } else {
            result.completeExceptionally(ex);
        }
    });
    return result;
}

它不漂亮,但它可以让你避免包装异常:

CompletableFuture<...> result = betterHandle(
    someService.call(request), 
    OCCException.class, 
    () -> makeDefaultResponse()
);