Reputation: 7434
I have a sequence of I/O operations (DB, I/O devices...) I need to run in sequence.
@SafeVarargs
public final CompletableFuture<Boolean> execute(final Supplier<Boolean>... methods)
{
CompletableFuture<Boolean> future = null;
for (Supplier<Boolean> method : methods)
{
if (future == null)
{
future = CompletableFuture.supplyAsync(method, threadPool);
}
else
{
future.thenCombineAsync(CompletableFuture.supplyAsync(method, threadPool), (result, currentResult) -> result && currentResult,
threadPool);
}
}
return future.exceptionally(this::onException);
}
My code executes randomly.
Upvotes: 0
Views: 3385
Reputation: 20579
Your current solution calls supplyAsync()
immediately and later tries to combine the results.
If you want to guarantee sequential execution, you should use thenApply()
or thenCompose()
instead of thenCombine()
:
for (Supplier<Boolean> method : methods)
{
if (future == null)
{
future = CompletableFuture.supplyAsync(method, threadPool);
}
else
{
future = future.thenApplyAsync(result -> result && method.get(), threadPool);
}
}
Note that this will not call the method.get()
on the next suppliers if any of them returns false, since &&
is short-circuiting. You could use a single &
to force the call anyway, or swap the parameters.
This already combines all the boolean results at the end. You can add anything on the resulting future
after the loop, like more thenApply()
calls, or a blocking join()
call to retrieve the Boolean
.
Note that this loop can also easily be rewritten using streams:
future = Arrays.stream(methods)
.reduce(CompletableFuture.completedFuture(true),
(f, method) -> f.thenApplyAsync(result -> result && method.get()),
(f1, f2) -> f1.thenCombine(f2, (result1, result2) -> result1 && result2));
Upvotes: 3
Reputation: 1474
You could easily do so by using the Spotify CompletableFutures library: https://github.com/spotify/completable-futures
They provide some really useful tools for this, for example an allAsList
that returns a CompletableFuture<List<T>>
.
Upvotes: 0