问题
I have a function which takes futures Future[A]*
and I want it to return 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
}
And I also want the result future of type Future[List[A]]
becomes completed immediately after the list futures List[Future[A]]
have been completed.
That code doesn't work. I figure I should use flatMap
here because there should be 2 internal loops: one for the future and one for promise. But how?
I'd like not use for comprehension here because I'd like to understand the process at the deeper lever.
回答1:
You can use foldRight to achieve this:
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]
}
}
if any future in futures list fails, a <- fut will fail, resulting in accum being set to failed future.
If you want to avoid using for, you can either expand it to flatMap as follows:
accum.flatMap( list => fut.map(a => a :: list))
Or you can use, async-await (noting that it is still an experimental feature).
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
}
回答2:
This is already implemented for you:
def singleFuture[T](futures: List[Future[A]]): Future[List[A]] = Future.sequence(futures)
You can, of course, look at the implementation of sequence:
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())
}
This can be simplified if you only want to deal with lists, and not with anything that has 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)
}
}
来源:https://stackoverflow.com/questions/20278133/calculate-the-list-of-the-futures-and-and-return-the-result-future