1

I have a bit of an odd situation that doesn’t seem to allow this peg to fit into any of the widely established CompletableFuture holes.

  • Within a primary method that is evaluating returned booleans, I am wanting to allow calls to three different methods to complete asynchronously. Each of these three methods can return either a TRUE or a FALSE.
  • If any return a FALSE, I want the evaluation to drop the remainder and simply return that FALSE value. The point being, it can be any of the three, not necessarily the first.
  • But most importantly, I need the evaluation to wait until all three return TRUE before actually returning TRUE. This is critically important.

Right now, I am using a basic && chain to make this evaluation:

public boolean evaluateChecks() {
    return checkOne().Join() && checkTwo().Join() && checkThree().Join();
}

However this still does things in a specific order - if checkThree() is the first to return a FALSE value, it still has to wait until the prior two have provided their values before it gets evaluated, due to how the && fall-through works.

Right now all three methods return CompletableFuture<Boolean>, but I have no problem reverting these back into normal methods in order to run a CompletableFuture in the primary method that evaluates them.

I have looked at quite a few examples, but none seem to provide me with the functionality I need.

Ryuzaki L
  • 37,302
  • 12
  • 68
  • 98
R. Kåbis
  • 51
  • 7
  • Have you considered `CountDownLatch`? – daniu Nov 22 '19 at 17:49
  • I am unable to implement `Runnable` in this class or cleave this code off into a separate class, due to other specifics of the project. Could I still implement `CountDownLatch` without implementing `Runnable`? – R. Kåbis Nov 22 '19 at 17:58
  • `.Join()` ? what method is this? – ACV Nov 22 '19 at 18:05
  • Have you tried using `cancel()` instead of returning false? And then do some action on cancel event? Have you tries also this `anyOf(CompletableFuture>... cfs)`? – ACV Nov 22 '19 at 18:09

1 Answers1

2

Adapted from the answer by Didier L here:

Instead of using exceptionally and completeExceptionally, use thenAccept to complete the CompletableFuture returned by allOf().

For example, save the futures because you're about to chain actions on them:

CompletableFuture<Boolean> a = checkOne(), b = checkTwo(), c = checkThree();

Use CompletableFuture.allOf to wait for all of them to complete (presumably you're not expecting failures/exceptions) and transform the Void result into the expected Boolean value of true.

CompletableFuture<Boolean> allWithFailFast = CompletableFuture.allOf(a, b, c).thenApply(__ -> a.join() && b.join() && c.join());
// if there is an exception, it'll be propagated to the future returned by thenApply

With the returned CompletableFuture above, you can now complete it faster if any of the original futures completes with false.

Stream.of(a, b, c).forEach(f -> f.thenAccept(result -> {
    if (!result) {
        allWithFailFast.complete(false);
    }
}));

Depending on the order of completions and their result, either the allOf future will complete first with a result of evaluating the futures or one of the futures will return false and cause the allWithFailFast to complete with false.

If multiple futures complete with false, only the first call to

allWithFailFast.complete(false);

will do anything, the others will essentially be ignored.

Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
  • Hmmm... when I try to put a return inside of the foreach, I get an `unexpected return value`. Not entirely sure how to return either the early FALSE or the final TRUE. Or do I just return `allWithFailFast.join()` at the very end? – R. Kåbis Nov 22 '19 at 20:14
  • @R.Kåbis `forEach` accepts a `Consumer`. You don't need to return anything in there. The `allWithFailFast.complete(false)` accomplishes the fail-fast behavior. – Sotirios Delimanolis Nov 22 '19 at 20:16
  • Well, the overall method is throwing an error because it has no boolean to report back with. Nothing is being explicitly returned. From what I understand, I will need to put a `return allWithfailFast.join()` at the end of the method, _after_ the `Stream.of()`. – R. Kåbis Nov 22 '19 at 20:24
  • @R.Kåbis What method is that? If you need to return a `CompletableFuture`, then return the `allWithFailFast`, which is what you should be working with to get the final result. – Sotirios Delimanolis Nov 22 '19 at 20:26
  • If you look at my original code, the `evaluateChecks()` that contains the evaluation of the checks needs to return a plain boolean. And a plain boolean is what the code at the far end is expecting. – R. Kåbis Nov 22 '19 at 20:28
  • Call `join` on that future. – Sotirios Delimanolis Nov 22 '19 at 20:30