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?
What's a CompletableFuture? CompletableFuture is used for asynchronous programming in Java. Asynchronous programming is a means of writing non-blocking code by running a task on a separate thread than the main application thread and notifying the main thread about its progress, completion or failure.
A stage of a possibly asynchronous computation, that performs an action or computes a value when another CompletionStage completes. A stage completes upon termination of its computation, but this may in turn trigger other dependent stages.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With