If have 2 CompletionStages, I can combine them with thenCombine
method:
CompletionStage<A> aCompletionStage = getA(); CompletionStage<B> bCompletionStage = getB(); CompletionStage<Combined> combinedCompletionStage = aCompletionStage.thenCombine(bCompletionStage, (aData, bData) -> combine(aData, bData));
If I have 3 or more CompletionStages, I can make a chain of thenCombine
methods, but I have to use temporary objects to pass results. For example, here is a solution using Pair
and Triple
from the org.apache.commons.lang3.tuple
package:
CompletionStage<A> aCompletionStage = getA(); CompletionStage<B> bCompletionStage = getB(); CompletionStage<C> cCompletionStage = getC(); CompletionStage<D> dCompletionStage = getD(); CompletionStage<Combined> combinedDataCompletionStage = aCompletionStage.thenCombine(bCompletionStage, (Pair::of)) .thenCombine(cCompletionStage, (ab, c) -> Triple.of(ab.getLeft(), ab.getRight(), c)) .thenCombine(dCompletionStage, (abc, d) -> combine(abc.getLeft(), abc.getMiddle(), abc.getRight(), d));
Is there a better way to combine results from multiple CompletionStages?
Advertisement
Answer
The only way to combine multiple stages that scales well with a growing number of stages, is to use CompletableFuture
. If your CompletionStage
s aren’t CompletableFuture
s you may still convert them using .toCompletableFuture()
:
CompletableFuture<A> aCompletionStage = getA().toCompletableFuture(); CompletableFuture<B> bCompletionStage = getB().toCompletableFuture(); CompletableFuture<C> cCompletionStage = getC().toCompletableFuture(); CompletableFuture<D> dCompletionStage = getD().toCompletableFuture(); CompletionStage<Combined> combinedDataCompletionStage = CompletableFuture.allOf( aCompletionStage, bCompletionStage, cCompletionStage, dCompletionStage) .thenApply(ignoredVoid -> combine( aCompletionStage.join(), bCompletionStage.join(), cCompletionStage.join(), dCompletionStage.join()) );
This contains more boilerplate than combining two stages via thenCombine
but the boilerplate doesn’t grow when adding more stages to it.
Note that even with your original thenCombine
approach, you don’t need a Triple
, a Pair
is sufficient:
CompletionStage<Combined> combinedDataCompletionStage = aCompletionStage.thenCombine(bCompletionStage, (Pair::of)).thenCombine( cCompletionStage.thenCombine(dCompletionStage, Pair::of), (ab, cd) -> combine(ab.getLeft(), ab.getRight(), cd.getLeft(), cd.getRight()));
Still, it doesn’t scale well if you want to combine more stages.
An in-between solution (regarding complexity) might be:
CompletionStage<Combined> combinedDataCompletionStage = aCompletionStage.thenCompose( a -> bCompletionStage.thenCompose(b -> cCompletionStage.thenCompose( c -> dCompletionStage.thenApply(d -> combine(a, b, c, d)))));
That’s simpler in its structure but still doesn’t scale well with more more stages.