Не существует такой вещи, как «подавление исключения».Когда вы вызываете exceptionally
, вы создаете новое будущее, которое будет завершено с результатом предыдущего этапа или результатом оценки функции, если предыдущий этап был выполнен исключительно.Предыдущий этап, т. Е. Будущее, на которое вы вызываете exceptionally
, не затрагивается.
Это относится к всем методам, связывающим зависимую функцию или действие.Каждый из этих методов создает новое будущее, которое будет завершено в соответствии с документацией.Ни один из них не влияет на существующее будущее, для которого вы вызываете метод.
Возможно, это станет намного понятнее в следующем примере:
CompletableFuture<String> f1 = CompletableFuture.supplyAsync(() -> {
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
return "a string";
});
CompletableFuture<Integer> f2 = f1.thenApply(s -> {
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(2));
return s.length();
});
f2.thenAccept(i -> System.out.println("result of f2 = "+i));
String s = f1.join();
System.out.println("result of f1 = "+s);
ForkJoinPool.commonPool().awaitQuiescence(1, TimeUnit.DAYS);
Здесь должно быть ясно, что результатиз зависимой стадии, Integer
, не может заменить результат предварительной стадии, String
.Это просто два разных фьючерса с разными результатами.И поскольку вызов join()
для f1
запросов на результат первого этапа, он не зависит от f2
и, следовательно, даже не ожидает его завершения.(Это также причина, по которой код ожидает окончания всех фоновых операций в конце).
Использование exceptionally
не отличается.Может показаться странным, что следующий этап имеет такой же тип и даже тот же результат в неисключительном случае, но это не меняет того факта, что существует два различных этапа.
static void report(String s, CompletableFuture<?> f) {
f.whenComplete((i,t) -> {
if(t != null) System.out.println(s+" completed exceptionally with "+t);
else System.out.println(s+" completed with value "+i);
});
}
CompletableFuture<Integer> f1 = CompletableFuture.supplyAsync(() -> {
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
throw new IllegalArgumentException("Error for testing");
});
CompletableFuture<Integer> f2 = f1.exceptionally(t -> 42);
report("f1", f1);
report("f2", f2);
ForkJoinPool.commonPool().awaitQuiescence(1, TimeUnit.DAYS);
Похоже, широко распространено мышление о CompletableFuture
методах цепочки, которые являются своего рода конструктором для единого будущего, что, к сожалению, ошибочно.Еще одной ошибкой является следующая ошибка:
CompletableFuture<?> f = CompletableFuture.supplyAsync(() -> {
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
System.out.println("initial stage");
return "";
}).thenApply(s -> {
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
System.out.println("second stage");
return s;
}).thenApply(s -> {
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
System.out.println("third stage");
return s;
}).thenAccept(s -> {
System.out.println("last stage");
});
f.cancel(true);
report("f", f);
ForkJoinPool.commonPool().awaitQuiescence(1, TimeUnit.DAYS);
Как объяснено, каждый цепочечный метод создает новую стадию, поэтому для получения приемлемой ссылки на стадию, возвращаемую последним цепочечным методом, т.е.конечный результат.Но отмена этого этапа приведет только к отмене этого последнего этапа и ни одного из обязательных этапов.Кроме того, после отмены последний этап больше не зависит от других этапов, поскольку он уже завершен путем отмены и может сообщить об этом исключительном результате, в то время как другие, теперь не связанные этапы, все еще оцениваются в фоновом режиме.