1

I need to call a method with different input parameters (like id) asynchronously, the method returns true or false, and the false output is not desirable, so I should wait for a true output. As soon as I get a true one, I do not care about the other calls, just I need to know the true output is corresponding with which input (id).

I call this piece of code, how should I know the input id of responded method? It might have a more completableFuture. The next question is in case of getting false, how can I skip and wait for getting true, because I need to recieve true from one of the myService.myMethod(int input)?

CompletableFuture<Boolean> completableFuture= CompletableFuture.supplyAsync(() -> myService.myMethod(1));
CompletableFuture<Boolean> completableFuture2= CompletableFuture.supplyAsync(() -> myService.myMethod(2));
CompletableFuture<Boolean> completableFuture3= CompletableFuture.supplyAsync(() -> myService.myMethod(3));
CompletableFuture<Boolean> completableFuture4= CompletableFuture.supplyAsync(() -> myService.myMethod(4));

CompletableFuture<Object> result =
        CompletableFuture.anyOf(completableFuture, completableFuture2,completableFuture3,,completableFuture4).thenApply(s -> s);
Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
  • 2
    `CompletableFuture` isn't really fit for your requirement (the first with a specific result) because all of its combining functionality is either "wait for all" (e.g. `thenCombine`), or "wait for any, but the status is ignored" (`anyOf`). https://openjdk.org/jeps/428 would provide a better solution, but that won't be finalized for at least one or two more Java releases. A custom solution is probably needed. – Rob Spoor Dec 26 '22 at 18:45
  • If you know that at least one of these results will be true, you could use an `AtomicReference` to store the first result (`compareAndSet(null, id)` will ignore any update except for the first), and a `CountDownLatch` with count 1 to wait for the result. If all results can be false, you'll need some more complex code, because the `CountDownLatch` may never have its `countDown` method called. – Rob Spoor Dec 26 '22 at 18:47
  • In the case, all results can be false. – Pooya Mirzapour Dec 26 '22 at 20:44
  • Does this answer your question? [CompletableFuture how to return first FALSE or wait until all are completed to return TRUE](https://stackoverflow.com/questions/58999254/completablefuture-how-to-return-first-false-or-wait-until-all-are-completed-to-r) – Didier L Dec 29 '22 at 14:33

1 Answers1

1

The fact that each result can be false makes this a bit tricky, because you can't abort too early. The following should work; it lets each CompletableFuture publish its results to a queue, as well as a sentinel value that indicates that all work is done. You then take the first element from the queue that was successful; if there is none, you know that all results are false if you read the sentinel value.

record Result (int id, boolean value) {}
final Result sentinel = new Result(Integer.MIN_VALUE, false);

// one more than the number of results, for the sentinel
BlockingDeque<Result> results = new LinkedBlockingDeque<>(5);
// the number of results exactly
CountDownLatch latch = new CountDownLatch(4);

CompletableFuture.runAsync(() -> {
    Record record = new Record(1, myService.myMethod(1));
    results.add(record);
    latch.countDown();
});
// etc for 2-4
CompletableFuture.runAsync(() -> {
    try {
        latch.await();
    } catch (InterruptedException e) {
        // log or something?
    } finally {
        results.add(sentinel);
    }
});

Result result = results.take();
while (!result.value() && !result.equals(sentinel)) {
    result = results.take();
}

The sentinel is only added once each result has been published (due to the CountDownLatch), ensuring it's always the last element.

When the loop ends, if result.equals(sentinel), all results were false. Otherwise, result contains the id for the first available successful result.

Rob Spoor
  • 6,186
  • 1
  • 19
  • 20