迭代List并等待Scala中的Future完成

时间:2018-02-07 08:38:11

标签: scala future

我有以下情况:

  • List[Building]集合,我将致电buildings
  • 一个def getAddress(building: Building): Future[Address]函数
  • 结果应该是List[BuildingWithAddress]集合

我想迭代List[Building]并为每个元素调用getAddress进行迭代并等待它完成,以便我可以创建一个BuildingWithAddress类型的新对象并将其存储在然后我会回到来电者那里。

我以为我使用了理解,但事实证明,这条线上的某些东西确实有效:

for {
    building <- listOfBuildings
    address <- getAddress(building)
    buildingWithAddress = BuildingWithAddress(name = building.name, town = address.town)
} yield buildingWithAddress

我还考虑使用flatMap迭代列表,然后对地址执行相同操作,但类型不同,并且无法正常工作。

我尝试使用forEach,但是再次forEach不等待未来完成。

这个简单用例的解决方案是什么?

2 个答案:

答案 0 :(得分:2)

你通常不能在一个for理解中组合不同的monad(除了scala的集合之外)。在这里,您希望组合FutureList monad,这不能通过这种方式完成。 如果你想以“顺序”方式执行此操作(等待以前的操作在开始新操作之前完成),则需要使用scalaz(或cat)中的ListT monad变换器,如下所示:

import scalaz.ListT
import scalaz.std.scalaFuture._

implicit executor: ExecutionContext = ...

def fromList[A](x: List[A]) = ListT(Future.successful(x))
def single[A](x: Future[A]) = ListT(x.map(List(_)))

(for {
  building <- fromList(listOfBuildings)
  address <- single(getAddress(building))
  buildingWithAddress = BuildingWithAddress(name = building.name, town = address.town)
} yield buildingWithAddress).run

根据需要,这将产生Future[List[...]]。 如果您可以并行调用getAddress函数,则可以选择其他解决方案:

Future.traverse(listOfBuildings)(building =>
  getAddress(building).map(address =>
    BuildingWithAddress(name = building.name, town = address.town)))

这遍历列表“applicatively”(意思是:并行)。

答案 1 :(得分:2)

使用Future.sequence

import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global

val l = List(1,2,3)
val futureList: Seq[Future[Int]] = l.map(e=>Future(e))

//conversion List[Future[T]] => Future[List[T]]
val singleFuture: Future[Seq[Int]] = Future.sequence(futureList)
singleFuture.map(_.length)
相关问题