8

我正在通过 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());

同样,有没有一种方法可以在没有 wrap-unwrap 的情况下复制番石榴的 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);
    });
4

2 回答 2

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获取源未来作为附加参数的 a 将非常有用:

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)
于 2014-08-18T16:44:10.550 回答
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()
);
于 2014-08-16T22:34:49.830 回答