Предположим, что у меня есть асинхронное вычисление, например:
CompletableFuture
.supplyAsync(() -> createFoo())
.thenAccept(foo -> doStuffWithFoo(foo));
Есть ли хороший способ предоставить значение по умолчанию для foo, если поставщик async истекает в соответствии с определенным таймаутом? В идеале такая функциональность также попытается отменить медленного поставщика. Например, существует ли стандартная библиотечная функциональность, похожая на следующий гипотетический код:
CompletableFuture
.supplyAsync(() -> createFoo())
.acceptEither(
CompletableFuture.completedAfter(50, TimeUnit.MILLISECONDS, DEFAULT_FOO),
foo -> doStuffWithFoo(foo));
Или, может быть, даже лучше:
CompletableFuture
.supplyAsync(() -> createFoo())
.withDefault(DEFAULT_FOO, 50, TimeUnit.MILLISECONDS)
.thenAccept(foo -> doStuffWithFoo(foo));
Я знаю о get(timeout, unit)
, но мне интересно, есть ли более стандартный стандартный способ применения тайм-аута в асинхронном и реактивном режиме, как это предлагается в коде выше.
EDIT: здесь решение, которое вдохновлено Java 8: Обязательно проверяет обработку исключений в лямбда-выражениях. Почему обязательный, а не факультативный?, но, к сожалению, он блокирует поток. Если мы полагаемся на createFoo(), чтобы асинхронно проверять тайм-аут и бросать свой собственный тайм-аут, он будет работать без блокировки потока, но возлагает больше бремя на создателя поставщика и будет по-прежнему иметь затраты на создание исключения (которое может быть дорогим без "быстрого броска" )
static <T> Supplier<T> wrapped(Callable<T> callable) {
return () -> {
try {
return callable.call();
} catch (RuntimeException e1) {
throw e1;
} catch (Throwable e2) {
throw new RuntimeException(e2);
}
};
}
CompletableFuture
.supplyAsync(wrapped(() -> CompletableFuture.supplyAsync(() -> createFoo()).get(50, TimeUnit.MILLISECONDS)))
.exceptionally(e -> "default")
.thenAcceptAsync(s -> doStuffWithFoo(foo));