RazrBos
RazrBos

Reputation: 23

Execute code based on result of two parallel completable futures

I have a scenario where in I have to issue two REST calls that return a value each based on the current system state, and based on those two values, have to trigger a final clean up task asynchronously - the flow of control being more like a 'Y' scenario . I have looked through the CompletableFuture interface, and is unable to find a way to accomplish this in a non-blocking fashion

I have tried this, and cant seem to find a way to get it working

// Verify task status
    CompletableFuture<AuditResult> checkOneFuture =
        CompletableFuture.supplyAsync(() -> dummyService.fetchSystemState(var1, var2),
                         executorService);

    CompletableFuture<AuditResult> checkTwoFuture =
        CompletableFuture.supplyAsync(() -> dummyService.fetchSystemState(var1, var3),
                         executorService);

    CompletableFuture<CompletableFuture<Boolean>> cleanUpFuture =
        checkOneFuture.thenCombineAsync(checkTwoFuture, (check1, check2) -> {
          if (check1.getSuccess() && check2.getSuccess()){
            CompletableFuture<Boolean> cleanUpFutuer = CompletableFuture.supplyAsync(() -> cleanUp(check1.id), executorService);
            return syncFuture;
          } else {
            return CompletableFuture.completedFuture(false);
          }
        }, executorService);

cleanUpFuture.join();

The cleanUpFuture is obviously syntactically not correct, and I am trying to figure ways to get this scenario working. Please help

Upvotes: 0

Views: 1785

Answers (2)

daniu
daniu

Reputation: 14999

As Slaw says in his comment, why not just return boolean?

CompletableFuture<Boolean> cleanUpFuture =
    checkOneFuture.thenCombineAsync(checkTwoFuture, (check1, check2) -> {
        if (check1.getSuccess() && check2.getSuccess()) {
            return cleanUp(check1.id); // will be scheduled due to combineAsync
        } else {
            return false;
        }
    }, executorService);

Note: for a shorter version, you can do

(check1, check2) -> check1.getSuccess() && check2.getSuccess() && cleanUp(check1.id);

Upvotes: 2

Hassam Abdelillah
Hassam Abdelillah

Reputation: 2294

You can acheive this by a ForkJoinPool. Subdivizing your call in subtask by calling fork() and reassemble the whole with a join()

For this you have maybe to implements a RecursiveTask

EDIT : Using CompletableFuture

If your purpose is to run two async processings in parallel and trigger another on completion of the later then allOf() is the best method.

Here is an example :

public CompletableFuture<String> findSomeValue() {
    return CompletableFuture.supplyAsync(() -> {
    sleep(1);
    return "Niraj";
    });
}

@Test
public void completableFutureAllof() {      
List<CompletableFuture<String>> list = new ArrayList<>();
    IntStream.range(0, 5).forEach(num -> {
            list.add(findSomeValue());
});

CompletableFuture<Void> allfuture = CompletableFuture.allOf(list.toArray(new CompletableFuture[list.size()]));//Created All of object       
    CompletableFuture<List<String>> allFutureList = allfuture.thenApply(val -> {
    return list.stream().map(f -> f.join()).collect(Collectors.toList());
});     

CompletableFuture<String> futureHavingAllValues = allFutureList.thenApply(fn -> {
    System.out.println("I am here");
    return fn.stream().collect(Collectors.joining());});
    String concatenateString = futureHavingAllValues.join();        
    assertEquals("NirajNirajNirajNirajNiraj", concatenateString);
}

This is example and more explanations are provided in this article

Upvotes: 1

Related Questions