【问题标题】:Play Framework 2 - How can I await multiple WS responses without calling .get()?Play Framework 2 - 如何在不调用 .get() 的情况下等待多个 WS 响应?
【发布时间】:2013-12-11 18:24:39
【问题描述】:

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

假设我有需要Bar(不是F.Promise<Bar>)的遗留代码,并在下面调用apply(Foo foo) 来获取它。在apply 中,我想做一些并发的网络服务调用,等待响应,然后使用它们来创建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?
    }
}

【问题讨论】:

  • 你回答过这个问题吗?我处于类似的情况,我需要插入的 API 不是基于 Promise 的。
  • 我认为唯一的选择是调用.get(timeout),其中 timeout 是某个 TimeUnit 或重写客户端代码以使用 Promises。

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


【解决方案1】:

(我从没用过Java中的Akka,所以对你的代码细节没有完全理解,但还是希望能理解你的问题)

简短回答:您无法获得 Bar。而且你永远不应该调用 .get,因为它要么扼杀了使用 Futures/Promises 的所有优势(在最好的情况下),要么正如已经提到的,导致死锁并扼杀一切(在最坏的情况下)。一旦你在 Future[-] 中有东西,它总是留在 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)
    }
  }
}

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

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

【讨论】:

  • 你用第一句话“你无法获得酒吧”来回答它。我最终重新分解了所有内容以使用地图和平面地图。
【解决方案2】:

返回F.Promise&lt;Bar&gt;,而不是返回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;
    }
}

从那里开始,您可以在Promise 中使用mapflatMapfilterBar 进行操作,如Javadoc 中所述:http://www.playframework.com/documentation/2.2.x/api/java/play/libs/F.Promise.html

在异步工作时,您习惯于始终在未来的上下文中处理您的代码,这就是您保持代码异步的方式。

在 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
  }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-10-15
    • 1970-01-01
    • 1970-01-01
    • 2022-06-18
    • 1970-01-01
    • 2020-02-23
    • 1970-01-01
    相关资源
    最近更新 更多