将多个承诺组合成一个承诺 PlayFramework 2.4

Compose multiple promises into one promise PlayFramework 2.4

我正在尝试学习 Play Framework 2.4。我正在尝试获取使用 Promise 异步访问不同网页所需的时间。下面是代码:

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);

如您所见,我正在使用 get 函数获取请求的页面并将结果放入 Future Promise 中。那我convert/map它就长了。我无法做的是如何将这三个承诺合二为一,一旦所有三个承诺都被兑现 convert/map 就变成了 json 和 return 结果。在较早版本的 Play 中,它可以由 F.Promise.waitAll(google,yahoo,bing).map(...) 完成,但我无法在 Play 2.4 中完成。请指教

EDIT1:根据下面的答案,我使用了如下序列:

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));
            }
        });

但是,我收到无法解析 google.get() 方法并且无法应用 Json 的错误。我在这里错过了什么?

编辑 2. 使用 return ok((JsonNode) Json.toJson((Writes<Object>) data)); 修复了 Json 错误但是,我仍然无法解决之前的错误 google.get() 方法无法在行 data.put("google", google.get());

中解析

编辑 3. Play2.4 似乎没有 get() 方法,它 return 是 Promise 被赎回后的值。那我该用什么?

waitAll 已替换为 F.Promise.sequence

来自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

更新

关于后半题,不需要调用.get(),因为promises已经完成了

事实上,您可以去掉单独的 promise 变量,直接将它们传递到序列中。结果列表将包含相同顺序的结果(在这种情况下,首先是 Google,然后是 Yahoo,然后是 Bing)。

整个事情看起来应该是这样的:

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);
    }

}

最后,由于 Play 2.4 需要 Java8,这是玩转 lambda 的好机会!