1

I am trying to convert a blocking Play framework controller action that looks like this:

public Result testSync(String param1, String param2) {

    String result1 = <LONG-DB-QUERY>;
    if (result1 == null) {
        return internalServerError();
    }

    if (result1.equals("<SOME VALUE>")) {
        return ok(param1);
    }

    String result2 = <LONG-DB-QUERY>;
    return ok(result1 + result2);
}

into non blocking code using the Future interface, i.e. returning a CompletionStage<Result>

As you see I need both result1 and result2. I assume that I cannot use supplyAsync and thenCombine because result2 needs to be calculated only under certain circumstances.

antoniom
  • 3,143
  • 1
  • 37
  • 53

1 Answers1

1

OK, based on a similar answer this is how I managed to do that:

public CompletionStage<Result> testAsync(String param1, String param2) {

    CompletableFuture<Result> shortCut = new CompletableFuture<>();
    CompletableFuture<String> withChain = new CompletableFuture<>();

    CompletableFuture.runAsync(() -> {
        String result1 = <LONG-DB-QUERY>;
        if (result1 == null) {
            shortCut.complete(internalServerError());
            return;
        }

        if (result1.equals("<SOME VALUE>")) {
            shortCut.complete(ok(param1));
            return;
        }

        withChain.complete(result1);
    });

    return withChain
            .thenCombine(CompletableFuture.supplyAsync(() -> <LONG-DB-QUERY>), (newParam1, newParam2) -> ok(result1+result2))
            .applyToEither(shortCut, Function.identity());
}
antoniom
  • 3,143
  • 1
  • 37
  • 53
  • With this solution you always compute `result2` even if you don't use its result. Also, both queries are run in parallel so you might actually get the result of the second query instead of the first one, if it is faster – is that intended? – Didier L Mar 15 '18 at 10:05