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