【问题标题】:Iterating over a List and waiting for a Future to complete in Scala在 Scala 中迭代 List 并等待 Future 完成
【发布时间】:2018-02-07 17:51:12
【问题描述】:

我有以下场景:

  • 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 没有等待 Future 完成。

这样一个简单的用例有什么解决方案?

【问题讨论】:

    标签: scala future


    【解决方案1】:

    你通常不能在一个 for 理解中组合不同的 monad(除了 scala 的 collection-likes)。在这里,您想组合 Future 和 List monad,这是无法做到的。 如果您想以“顺序”方式执行此操作(在开始新操作之前等待以前的未来操作完成),您需要使用来自 scalaz(或猫)的 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)))
    

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

    【讨论】:

    • 最后一个代码中遗漏了一个参数,这里是固定版本:Future.traverse(listOfBuildings)(building =&gt; getAddress(building).map(address =&gt; BuildingWithAddress(name = building.name, town = address.town)))
    • 使用帖子底部的edit 按钮编辑/更正您的答案。比尝试在 cmets 中添加更正更好/更清晰。
    • @mpetruska 谢谢,我使用了你答案的并行解决方案,它很有魅力,而且绝对干净。
    【解决方案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)
    

    【讨论】:

      猜你喜欢
      • 2017-09-30
      • 1970-01-01
      • 1970-01-01
      • 2019-03-02
      • 2020-01-14
      • 2017-08-03
      • 1970-01-01
      • 2017-12-08
      • 2018-09-22
      相关资源
      最近更新 更多