Play/Scala: 进行未知数量的 I/O 并行调用,等待结果
Play/Scala: Making unknown number of I/O calls in parallell, watining for the results
所以,我阅读了关于并行理解的文章here。他给出了以下代码示例:
// Make 3 parallel async calls
val fooFuture = WS.url("http://foo.com").get()
val barFuture = WS.url("http://bar.com").get()
val bazFuture = WS.url("http://baz.com").get()
for {
foo <- fooFuture
bar <- barFuture
baz <- bazFuture
} yield {
// Build a Result using foo, bar, and baz
Ok(...)
}
到目前为止一切都很好,但是,我处于这样一种情况,我不知道我总是需要做多少 WS.get(),我希望它是动态的。例如:
val checks = Seq(callOne(param), callTwo(param))
来电地点:
def callOne(param: String): Future[Boolean] = {
// do something and return the Future with a true/false value
Future(true)
}
def callTwo(param: String): Future[Boolean] = {
// do something and return the Future with a true/false value
Future(false)
}
所以,我的问题是,我应该如何在 for-yield 中对 WS 调用(或与此相关的数据库查询)的序列结果做出反应?
我已经给出了两个调用示例,但我希望相同的代码能够并行处理 1 到多个调用,并在 for-yield 中收集结果以最终继续做其他事情。
重要提示:所有调用都应并行执行,最快的调用将在最慢的调用之前完成,而不考虑调用的顺序。
Future.sequence 可能是您想要的。
用法示例:
val futures = List(WS.url("http://foo.com").get(), WS.url("http://bar.com").get())
Future.sequence(futures) # => Transforms a Seq[Future[_]] to Future[Seq[_]]
来自 Future.sequence 的未来 returns 将在输入序列中的所有未来完成之前完成。
奖金:
如果您的期货是异构类型的,并且您需要保留该类型,则可以使用 Hlist。我编写了以下代码片段,它将获取一个 Hlist 期货,并将其转换为一个包含一个 Hlist 已解析值的 Future:
import shapeless._
import scala.concurrent.{ExecutionContext,Future}
object FutureHelpers {
object FutureReducer extends Poly2 {
import scala.concurrent.ExecutionContext.Implicits.global
implicit def f[A, B <: HList] = at[Future[A], Future[B]] { (f, resultFuture) =>
for {
result <- resultFuture
value <- f
} yield value :: result
}
}
// Like Future.sequence, but for HList
// hsequence(Future { 1 } :: Future { "string" } :: HNil)
// => Future { 1 :: "string" :: HNil }
def hsequence[T <: HList](hlist: T)(implicit
executor: ExecutionContext,
folder: RightFolder[T, Future[HNil], FutureReducer.type]) = {
hlist.foldRight(Future.successful[HNil](HNil))(FutureReducer)
}
}
所以,我阅读了关于并行理解的文章here。他给出了以下代码示例:
// Make 3 parallel async calls
val fooFuture = WS.url("http://foo.com").get()
val barFuture = WS.url("http://bar.com").get()
val bazFuture = WS.url("http://baz.com").get()
for {
foo <- fooFuture
bar <- barFuture
baz <- bazFuture
} yield {
// Build a Result using foo, bar, and baz
Ok(...)
}
到目前为止一切都很好,但是,我处于这样一种情况,我不知道我总是需要做多少 WS.get(),我希望它是动态的。例如:
val checks = Seq(callOne(param), callTwo(param))
来电地点:
def callOne(param: String): Future[Boolean] = {
// do something and return the Future with a true/false value
Future(true)
}
def callTwo(param: String): Future[Boolean] = {
// do something and return the Future with a true/false value
Future(false)
}
所以,我的问题是,我应该如何在 for-yield 中对 WS 调用(或与此相关的数据库查询)的序列结果做出反应?
我已经给出了两个调用示例,但我希望相同的代码能够并行处理 1 到多个调用,并在 for-yield 中收集结果以最终继续做其他事情。
重要提示:所有调用都应并行执行,最快的调用将在最慢的调用之前完成,而不考虑调用的顺序。
Future.sequence 可能是您想要的。
用法示例:
val futures = List(WS.url("http://foo.com").get(), WS.url("http://bar.com").get())
Future.sequence(futures) # => Transforms a Seq[Future[_]] to Future[Seq[_]]
来自 Future.sequence 的未来 returns 将在输入序列中的所有未来完成之前完成。
奖金:
如果您的期货是异构类型的,并且您需要保留该类型,则可以使用 Hlist。我编写了以下代码片段,它将获取一个 Hlist 期货,并将其转换为一个包含一个 Hlist 已解析值的 Future:
import shapeless._
import scala.concurrent.{ExecutionContext,Future}
object FutureHelpers {
object FutureReducer extends Poly2 {
import scala.concurrent.ExecutionContext.Implicits.global
implicit def f[A, B <: HList] = at[Future[A], Future[B]] { (f, resultFuture) =>
for {
result <- resultFuture
value <- f
} yield value :: result
}
}
// Like Future.sequence, but for HList
// hsequence(Future { 1 } :: Future { "string" } :: HNil)
// => Future { 1 :: "string" :: HNil }
def hsequence[T <: HList](hlist: T)(implicit
executor: ExecutionContext,
folder: RightFolder[T, Future[HNil], FutureReducer.type]) = {
hlist.foldRight(Future.successful[HNil](HNil))(FutureReducer)
}
}