Play Framework 2 - 如何在不调用.get()的情况下等待多个WS响应?

时间:2013-12-11 18:24:39

标签: java web-services concurrency playframework-2.0 akka

一些Play Framework开发人员said“永远不会在Promise上调用get(),因为它可能会导致死锁。”好。

假设我的遗留代码需要Bar(而不是F.Promise<Bar>)并在下面调用apply(Foo foo)才能获得它。在apply内,我想做一些并发的webservice调用,等待响应,然后使用它们来制作Bar。如果不调用futureBar.get(),我怎么能这样做,再次假设不能选择返回F.Promise<Bar>

public class Func implements F.Function<Foo,Bar> {

    @Override
    public Bar apply(Foo foo) throws Throwable {
        F.Promise<WS.Response> response1 = WS.url("http://google.com").get();
        F.Promise<WS.Response> response2 = WS.url("http://yahoo.com").get();
        F.Promise<List<WS.Response>> responses = F.Promise.sequence(response1, response2);
        F.Promise<Bar> futureBar = responses.map(new F.Function<List<WS.Response>, Bar>() {

            @Override
            public Bar apply(List<WS.Response> o) throws Throwable {
                //some code;
                return bar;
            }
        });
        //How can I return Bar without calling get?
    }
}

2 个答案:

答案 0 :(得分:1)

(我从未使用过来自Java的Akka,所以不要完全理解你的代码细节,但我希望我能理解你的问题)

简短的回答:你无法获得Bar。并且你永远不应该调用.get,因为它要么杀死使用Futures / Promises(在最好的情况下)的所有优点,要么就像已经提到的那样导致死锁并杀死所有东西(在最坏的情况下)。一旦你在Future [ - ]中有东西,它总是留在未来[ - ],就没有办法了。如果您有遗留代码执行此操作:

bar = Func.apply(foo);
doSomethingWithBar(bar)

你突然发现你的Func不再同步(不立即返回),而是异步(发出一堆长请求),你必须接受你的新异步版本的Func不能和' ;'

所以你必须重新编程';'

当然,你不是自己动手,因为你已经有了Future [ - ] monad,所以你只需要替换旧的';'由新的';'即map / flatMap,取决于你的doSomethingWithBar是否同步:

Func.apply(foo).map{ bar =>
  doSomethingWithBar(bar)
}

一般来说,如果你之前有类似的东西:

y = asynch_func1(x);
z = asynch_func2(x, y);
w = synch_func3(x, y, z);
v = asynch_func4(x, y, z, w);
print(v)

突然发现你的函数asynch_func1,asynch_func2,asynch_func4现在是异步的并且需要很长时间才能完成(虽然synch_func3保持同步,就像例子一样),你把它重写为:

asynch_func1(x).flatMap{ y =>
  asynch_func2(x, y).map{ z =>
    synch_func3(x,y,z).flatMap{ w =>
      v = asynch_func4(x, y, z, w)
      print(v)
    }
  }
}

我想它会看起来非常可怕(没有理解和Java语法......),但只要你有概念清晰度和理解你只是用map / flatMap替换分号,你应该是能够非常快速地调整遗留代码,而且(太)头痛。

我希望我已经回答了正确的问题。

答案 1 :(得分:1)

不是返回Bar,而是返回F.Promise<Bar>

public class Func implements F.Function<Foo,F.Promise<Bar>> {

    @Override
    public F.Promise<Bar> apply(Foo foo) throws Throwable {
        F.Promise<WS.Response> response1 = WS.url("http://google.com").get();
        F.Promise<WS.Response> response2 = WS.url("http://yahoo.com").get();
        F.Promise<List<WS.Response>> responses = F.Promise.sequence(response1, response2);
        F.Promise<Bar> futureBar = responses.map(new F.Function<List<WS.Response>, Bar>() {

            @Override
            public Bar apply(List<WS.Response> o) throws Throwable {
                //some code;
                return bar;
            }
        });

        return futureBar;
    }
}

从那里开始,您使用Bar,使用PromisemapflatMapfilter内操作,如Javadoc所述:{{3 }}

异步工作时,您习惯于在将来的环境中处理代码,这就是保持代码异步的方法。

在Scala中,你会这样做:

def apply(foo: Foo): Future[Bar] = {
  for {
    response1 <- WS.url("http://google.com").get
    response2 <- WS.url("http://yahoo.com").get
  } yield {
    // some code
    bar
  }
}