Вы, похоже, упускаете важный момент.Когда вы связываете в цепочку зависимую функцию, вы не изменяете будущее, в котором вы вызываете метод сцепления.
Вместо этого каждый из этих методов возвращает завершение new Этап, представляющий зависимое действие.
Так как вы присоединяете два зависимых действия к result
, которые представляют task
, переданное supplyAsync
, между этими двумя действиями нет никакой связи.Они могут выполняться в произвольном порядке и даже одновременно в разных потоках.
Поскольку вы нигде не сохраняете будущее, возвращаемое thenApplyAsync
, результат его оценки в любом случае будет потерян.Предполагая, что ваша функция возвращает результат того же типа, что и T
, вы можете использовать
if(x == 1) {
result = result.thenApplyAsync(t -> {
return null;
});
}
, чтобы заменить потенциально завершенное будущее новым будущим, которое завершается только тогда, когда результат указанной функции имеетбыли оценены.Runnable, зарегистрированный в исходном будущем через thenRun
, все еще не зависит от этого нового будущего.Обратите внимание, что thenApplyAsync
без исполнителя всегда будет использовать исполнителя по умолчанию, независимо от того, какой исполнитель использовался для завершения другого будущего.
Если вы хотите убедиться, что Runnable
был успешно выполнен перед любым другимstage, вы можете использовать
CompletableFuture<T> result = CompletableFuture.supplyAsync(task, executor);
CompletableFuture<Void> thenRun = result.thenRun(() -> {
//...
});
result = result.thenCombine(thenRun, (t,v) -> t);
Альтернативой будет
result = result.whenComplete((value, throwable) -> {
//...
});
, но здесь код всегда будет выполняться даже в исключительном случае (который включает отмену).Вам нужно проверить, является ли throwable
значением null
, если вы хотите выполнить код только в успешном случае.
Если вы хотите убедиться, что запускаемый файл запускается после обоих действий,Простейшей стратегией было бы связать ее после оператора if
, когда определен финальный этап завершения:
if(x == 1) {
result = result.thenApplyAsync(t -> {
return null;
});
}
result.thenRun(() -> {
//...
});
Если это не вариант, вам понадобится неполное будущее, которое вы можете выполнить в любомрезультат:
CompletableFuture<T> result = CompletableFuture.supplyAsync(task, executor);
//...
CompletableFuture<T> finalStage = new CompletableFuture<>();
finalStage.thenRun(() -> {
//...
});
// ...
if(x == 1) {
result = result.thenApplyAsync(t -> {
return null;
});
}
result.whenComplete((v,t) -> {
if(t != null) finalStage.completeExceptionally(t); else finalStage.complete(v);
});
У finalStage
изначально нет определенного способа завершения, но мы все равно можем связывать зависимые действия.Как только мы узнаем фактическое будущее, мы можем связать обработчик, который завершит наш finalStage
с любым результатом, который у нас есть.
В качестве последнего замечания, методы без …Async
, такие как thenRun
обеспечить наименьший контроль над потоком оценки.Они могут выполняться в любом потоке, завершившем будущее, например, в одном из потоков executor
в вашем примере, но также непосредственно в потоке, вызывающем thenRun
, и даже менее интуитивно понятным, в вашем исходном примере исполняемый файл может быть выполненво время несвязанного thenApplyAsync
вызова.