当函数依赖于未来的某些结果时,我一次又一次地挣扎 . 这通常归结为像Future [Seq [Future [MyObject]]]这样的结果
为了摆脱这一点,我现在在辅助函数中使用Await来获取非未来对象并减少嵌套 .
看起来像这样
def findAll(page: Int, perPage: Int): Future[Seq[Idea]] = {
val ideas: Future[Seq[Idea]] = collection.find(Json.obj())
// [...]
ideas.map(_.map { // UGLY?
idea => {
// THIS RETURNED A Future[JsObject] before
val shortInfo: JsObject = UserDao.getShortInfo(idea.user_id)
idea.copy(user_data = Some(shortInfo))
}
})
}
这段代码有效,但对我来说它看起来很hacky . 这两个 Map 调用是另一个缺陷 . 我花了好几个小时试图弄清楚如何保持这种完全异步并返回一个简单的未来Seq . 如何使用Play2最佳实践解决这个问题?
Edit 使用例更清晰:
我有一个来自mongodb(reactivemongo)的对象A,并希望将来自另一个调用的信息添加到mongodb getShortInfo
. 这是一个经典的"get user for this post"案例,可以通过RDBMS中的连接来解决 . 由于对db的调用, getShortInfo
自然会产生Future . 为了减少 findAll
中的嵌套,我使用了Await() . 这是一个好主意吗?
findAll
从异步Play动作调用,转换为Json并通过线路发送 .
def getIdeas(page: Int, perPage: Int) = Action.async {
for {
count <- IdeaDao.count
ideas <- IdeaDao.findAll(page, perPage)
} yield {
Ok(Json.toJson(ideas))
}
}
所以我认为从findAll返回 Seq[Future[X]]
将不会带来更好的性能,因为我必须等待结果 . 它是否正确?
简而言之:使用Future调用返回一个Sequence,使用结果的每个元素创建另一个Future调用,以不会发生阻塞情况的方式将结果返回给异步操作 .
2 回答
你应该知道的Future伴侣对象上的两个方便的功能可以在这里帮助,第一个,更容易包裹你的头是
Future.sequence
. 它需要一个未来的序列并返回一个序列的未来 . 如果以Future[Seq[Future[MyObject]]]
结尾,请调用result
. 然后你可以用result.map(Future.sequence(_))
将其更改为Future[Future[Seq[MyObject]]]
然后为任何X折叠
Future[Future[X]]
,你可以运行"result.flatMap(identity)",事实上,只要M
具有flatMap
,你就可以为任何M[M[X]]
创建M[X]
.这里另一个有用的功能是
Future.traverse
. 它基本上是采用Seq[A]
,将其映射到Seq[Future[B]]
,然后运行Future.sequence以获得Future[Seq[B]]
的结果 . 所以在您的示例中,您将拥有:但是,很多时候当你运行flatMap(标识)时,你可能会将 Map 变成flatMap,这就是这里的情况:
Akka documentation对如何处理期货成分有一个很好的概述 . 一般来说,它概述了scala.concurrent.Future中的四种方法,可用于将期货的组合减少为单个Future实例:
Future.sequence
Future.traverse
Future.fold
Future.reduce