kayz1
kayz1

Reputation: 7434

Run async actions in sequence

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.

  1. What can I do to ensure order?
  2. How can I combine results in the end? If all were true, for instance?
  3. To apply a callback after everything is complete to check the result?

Upvotes: 0

Views: 3385

Answers (2)

Didier L
Didier L

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

dirkvranckaert
dirkvranckaert

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

Related Questions