Ответ 1
Вы можете сделать это следующим образом:
public CompletionStage<SomeResult> someMethod(SomeArgument someArgument) {
CompletableFuture<SomeResult> shortCut = new CompletableFuture<>();
CompletableFuture<ResultOfFirstOp> withChain = new CompletableFuture<>();
CompletableFuture.runAsync(() -> {
// loooooong operation
if (someCondition)
withChain.complete(validValue);
else
shortCut.complete(SomeResult.RESULT_1);
});
return withChain
.thenCompose(result -> someMethodThatReturnsACompletionStage(result))
.thenApply(result ->
result.someCondition()? SomeResult.RESULT_2: SomeResult.RESULT_3)
.applyToEither(shortCut, Function.identity());
}
Вместо одного CompletableFuture
мы создаем два, представляя различные пути выполнения, которые мы могли бы предпринять. Операция loooooong представляется в виде runnable then и намеренно завершает один из этих CompletableFuture
. Последующие этапы привязаны к кадру, представляющей выполненное условие, то оба пути выполнения соединяются на последнем шаге applyToEither(shortCut, Function.identity())
.
Будущее будущего shortCut
уже является типом конечного результата и будет завершено с RESULT_1
, результатом вашего пути прохождения null
, что приведет к немедленному завершению всей операции. Если вам не нравится зависимость между первым этапом и фактическим значением результата короткого замыкания, вы можете отменить его следующим образом:
public CompletionStage<SomeResult> someMethod(SomeArgument someArgument) {
CompletableFuture<Object> shortCut = new CompletableFuture<>();
CompletableFuture<ResultOfFirstOp> withChain = new CompletableFuture<>();
CompletableFuture.runAsync(() -> {
// loooooong operation
if (someCondition)
withChain.complete(validValue);
else
shortCut.complete(null);
});
return withChain
.thenCompose(result -> someMethodThatReturnsACompletionStage(result))
.thenApply(result ->
result.someCondition()? SomeResult.RESULT_2: SomeResult.RESULT_3)
.applyToEither(shortCut.thenApply(x -> SomeResult.RESULT_1), Function.identity());
}
Если ваш третий шаг не был образцовым, но выглядел точно так, как показано в вопросе, вы можете объединить его с шагом присоединения кодового пути:
public CompletionStage<SomeResult> someMethod(SomeArgument someArgument) {
CompletableFuture<ResultOfSecondOp> shortCut = new CompletableFuture<>();
CompletableFuture<ResultOfFirstOp> withChain = new CompletableFuture<>();
CompletableFuture.runAsync(() -> {
// loooooong operation
if (someCondition)
withChain.complete(validValue);
else
shortCut.complete(null);
});
return withChain
.thenCompose(result -> someMethodThatReturnsACompletionStage(result))
.applyToEither(shortCut, result -> result==null? SomeResult.RESULT_1:
result.someCondition()? SomeResult.RESULT_2: SomeResult.RESULT_3);
}
то мы пропустим только второй шаг, вызов someMethodThatReturnsACompletionStage
, но он все еще может стоять за длинной цепочкой промежуточных шагов, все пропущенные без необходимости развертывания ручного пропуска через nullcheck.