Ваша функция, переданная thenCompose
, возвращает уже завершенное будущее, то есть return CompletableFuture.completedFuture(NotUsed.getInstance());
, что позволяет немедленно перейти к зависимым этапам.Похоже, это противоречит оценке функции, переданной в templateIds.map(…)
, что, по-видимому, происходит асинхронно.
Как правило, следует избегать такой смеси этапов завершения и зависимости от побочных эффектов, особенно когда их асинхронная оценкане смоделирован как обязательный этап завершения.
Но вы можете обойти это, если у вас нет другого выбора:
return getTemplateIds(context, metricsClient, jdbcSession, content)
.thenCompose(
templateIds -> {
// create an initially uncompleted stage
CompletableFuture<Object> subStage = new CompletableFuture<>();
templateIds.map(
id ->
// do something and return CompletionStage<String>
.thenAccept(
tree -> {
templateTreeHolder.set(
(ObjectNode)
templateTreeHolder.get().set(id, Json.readTree(tree)));
System.out.println(
"From inner function: " + templateTreeHolder.get());
// complete when all work has been done
subStage.complete(null);
}));
// use this stage for dependent actions
return subStage;
})
.thenApply(
notUsed -> {
String includedTemplateIdsStr =
getKeysFromTemplateTree(templateTreeHolder.get()).toJavaList().toString();
System.out.println("From outer function: " + templateTreeHolder.get());
return Tuple.of(templateTreeHolder.get(), includedTemplateIdsStr);
});
В приведенном выше коде будущее никогда не будет завершено, если вашдействие завершается с исключением до попытки завершения.Общий шаблон будет выглядеть следующим образом:
CompletableFuture<Type> stage = new CompletableFuture<>();
…
try {
code that will eventually call complete on stage
}
catch(Throwable t) {
stage.completeExceptionally(t);
}
Но, конечно, это будет немного сложнее, когда код, который должен завершить этап, также несет асинхронную обработку, так что вам нужно защищать код, пытаясьпредставить действительный код завершения, а также фактический код завершения.
Итак, более сложная версия внутреннего кода будет выглядеть так:
CompletableFuture<Object> subStage = new CompletableFuture<>();
try {
templateIds.map(
id ->
// do something and return CompletionStage<String>
.thenAccept(
tree -> {
templateTreeHolder.set(
(ObjectNode)
templateTreeHolder.get().set(id, Json.readTree(tree)));
System.out.println(
"From inner function: " + templateTreeHolder.get());
})
.whenComplete((v,t) -> {
// complete when all work has been done
if(t != null) subStage.completeExceptionally(t);
else subStage.complete(v);
}));
} catch(Throwable t) {
subStage.completeExceptionally(t);
}
// use this stage for dependent actions
return subStage;
(возможно, «сделатьчто-то и вернуть CompletionStage »тоже нужно охранять с try { … } catch(Throwable t) { subStage.completeExceptionally(t); }
)