200ok
200ok

Reputation: 210

Compose multiple promises into one promise PlayFramework 2.4

I am trying to learn the Play Framework 2.4. I am trying to get the time it takes to access different webpages asynchronously using Promise. Below is the code for that:

final long start = System.currentTimeMillis();

F.Function<WSResponse,Long> timing = new F.Function<WSResponse, Long>() {
    @Override
    public Long apply(WSResponse wsResponse) throws Throwable {
        return System.currentTimeMillis()-start;
    }
};
F.Promise<Long> google = ws.url("http://www.google.com").get().map(timing);
F.Promise<Long> yahoo = ws.url("http://www.yahoo.com").get().map(timing);
F.Promise<Long> bing = ws.url("http://www.bing.com").get().map(timing);

As you can see I am using the get function to get the requested pages and putting the result in a Future Promise. Then I convert/map it to long. What I am not able to do is how do I compose these three promises into one and once all of the three promises are redeemed convert/map it to json and return the result. In earlier versions of Play it could have been done by F.Promise.waitAll(google,yahoo,bing).map(...) however I am unable to do it in Play 2.4. Please advice

EDIT1: Based on the answer below i used sequence like below:

return F.Promise.sequence(google, yahoo, bing).map(new F.Function<List<Long>, Result>() {
            @Override
            public Result apply(List<Long> longs) throws Throwable {
                Map<String, Long> data = new HashMap<String, Long>();
                data.put("google", google.get());
                data.put("yahoo", yahoo.get());
                data.put("bing", bing.get());
                return ok(Json.toJson(data));
            }
        });

However, i am getting error that google.get() method cannot be resolved and that Json cannot be applied. What am i missing here?

EDIT 2. The Json error was fixed by using return ok((JsonNode) Json.toJson((Writes<Object>) data)); However, i am still not able to resolve the earlier error that google.get() method cannot be resolved in the line data.put("google", google.get());

EDIT 3. It seems Play2.4 has no get() method which returns the value of a Promise once it has been redeemed. What should i use then?

Upvotes: 3

Views: 4547

Answers (1)

Steve Chaloner
Steve Chaloner

Reputation: 8202

waitAll has been replaced with F.Promise.sequence.

From the docs

public static <A> F.Promise<java.util.List<A>> sequence(java.lang.Iterable<F.Promise<A>> promises)

Combine the given promises into a single promise for the list of results. The sequencing operations are performed in the default ExecutionContext.

Parameters: promises - The promises to combine

Returns: A single promise whose methods act on the list of redeemed promises

Update

Regarding the second half of the question, you don't need to call .get() because the promises have already completed.

In fact, you can get rid of the individual promise variables and just pass them directly into the sequence. The resulting list will contain results in the same order (Google first, then Yahoo, then Bing, in this case).

The whole thing should look something like this:

package controllers;

import java.util.HashMap;
import java.util.Map;
import play.libs.F;
import play.libs.Json;
import play.libs.ws.WS;
import play.libs.ws.WSResponse;
import play.mvc.Controller;
import play.mvc.Result;
import play.mvc.Results;

public class Application extends Controller {

    public F.Promise<Result> index() {
        final long start = System.currentTimeMillis();
        final F.Function<WSResponse,Long> timing = response -> System.currentTimeMillis() - start;

        return F.Promise.sequence(WS.url("http://www.google.com").get().map(timing),
                                  WS.url("http://www.yahoo.com").get().map(timing),
                                  WS.url("http://www.bing.com").get().map(timing))
                        .map(list -> {
                            Map<String, Long> data = new HashMap<>();
                            data.put("google", list.get(0));
                            data.put("yahoo", list.get(1));
                            data.put("bing", list.get(2));
                            return data;
                        })
                        .map(Json::toJson)
                        .map(Results::ok);
    }

}

Finally, since Play 2.4 requires Java 8, this is a good opportunity to play around with lambdas!

Upvotes: 6

Related Questions