计算期货清单并返回结果未来

时间:2013-11-29 03:56:30

标签: scala concurrency future

我有一个功能,需要期货Future[A]*,我希望它返回Future[List[A]]

def singleFuture[T](futures: List[Future[A]]): Future[List[A]] = {
  val p = Promise[T]
  futures filter { _ onComplete { case x => p complete x /*????*/ } }
  p.future
} 

我还希望类型Future[List[A]]的结果未来在列表期货List[Future[A]]完成后立即完成。

该代码不起作用。我想我应该在这里使用flatMap因为应该有2个内部循环:一个用于未来,一个用于promise。但是如何?

我不想在这里使用进行理解,因为我想更深入地理解这个过程。

2 个答案:

答案 0 :(得分:8)

这已经为你实现了:

def singleFuture[T](futures: List[Future[A]]): Future[List[A]] = Future.sequence(futures)

当然,您可以查看序列的实现:

def sequence[A, M[_] <: TraversableOnce[_]](in: M[Future[A]])(implicit cbf: CanBuildFrom[M[Future[A]], A, M[A]], executor: ExecutionContext): Future[M[A]] = {
  in.foldLeft(Promise.successful(cbf(in)).future) {
    (fr, fa) => for (r <- fr; a <- fa.asInstanceOf[Future[A]]) yield (r += a)
  } map (_.result())
}

如果你只想处理列表,而不是任何有foldLeft的东西,这可以简化:

def sequence[A](in: List[Future[A]]): Future[List[A]] = {
  in.foldRight[Future[List[A]](Promise.successful(Nil) {
    (fa, fr) => for { r <- fr; a <- fa } yield (a :: r)
  }
}

答案 1 :(得分:3)

您可以使用foldRight来实现此目的:

def singleFuture[A](futures: List[Future[A]]): Future[List[A]] = {

    val p = Promise[List[A]]()
    p.success(List.empty[A])

    val f = p.future // a future containing empty list.

    futures.foldRight(f) { 
       (fut, accum) =>  // foldRight means accumulator is on right.

        for {
           list <- accum;  // take List[A] out of Future[List[A]]
           a    <- fut     // take A out of Future[A]
        }
        yield (a :: list)   // A :: List[A]
    }
}

如果期货清单中的任何未来失败,&lt; - fut将失败,导致将被设置为未来失败。

如果您想避免使用for,可以将其扩展为flatMap,如下所示:

  accum.flatMap( list => fut.map(a => a :: list))

或者您可以使用async-await(注意它仍然是一个实验性功能)。

def singleFuture[T](futures: List[Future[T]]): Future[List[T]] = async {

  var localFutures = futures
  val result = ListBuffer[T]()
  while (localFutures != Nil) {
        result += await { localFutures.head } 
        localFutures = localFutures.tail
  }
  result.toList
}