在 Scala 中迭代 List 并等待 Future 完成
Iterating over a List and waiting for a Future to complete in Scala
我有以下场景:
- 一个
List[Building]
合集我叫buildings
- 一个
def getAddress(building: Building): Future[Address]
函数
- 结果应该是
List[BuildingWithAddress]
集合
我想遍历 List[Building]
并为每个元素调用 getAddress
并等待它完成,以便我可以创建一个类型为 BuildingWithAddress
的新对象并存储它在一个集合中,然后我将 return 给调用者。
我以为我会使用 for-comprehension,但事实证明,沿着这条线的东西不会真正起作用:
for {
building <- listOfBuildings
address <- getAddress(building)
buildingWithAddress = BuildingWithAddress(name = building.name, town = address.town)
} yield buildingWithAddress
我也考虑过使用 flatMap
遍历列表,然后对地址执行相同的操作,但类型不同,这行不通。
我尝试使用 forEach
但是 forEach 又没有等待 Future 完成。
对于这样一个简单的用例,解决方案是什么?
您通常不能在单个 for
理解中组合不同的 monad(scala 类集合除外)。在这里你想组合 Future
和 List
monads,这不能通过这种方式完成。
如果您想以 "sequential" 方式执行此操作(在开始新操作之前等待以前的未来操作完成),您需要使用来自 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)))
这是遍历List"applicatively"(意思是:并行)
使用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)
我有以下场景:
- 一个
List[Building]
合集我叫buildings
- 一个
def getAddress(building: Building): Future[Address]
函数 - 结果应该是
List[BuildingWithAddress]
集合
我想遍历 List[Building]
并为每个元素调用 getAddress
并等待它完成,以便我可以创建一个类型为 BuildingWithAddress
的新对象并存储它在一个集合中,然后我将 return 给调用者。
我以为我会使用 for-comprehension,但事实证明,沿着这条线的东西不会真正起作用:
for {
building <- listOfBuildings
address <- getAddress(building)
buildingWithAddress = BuildingWithAddress(name = building.name, town = address.town)
} yield buildingWithAddress
我也考虑过使用 flatMap
遍历列表,然后对地址执行相同的操作,但类型不同,这行不通。
我尝试使用 forEach
但是 forEach 又没有等待 Future 完成。
对于这样一个简单的用例,解决方案是什么?
您通常不能在单个 for
理解中组合不同的 monad(scala 类集合除外)。在这里你想组合 Future
和 List
monads,这不能通过这种方式完成。
如果您想以 "sequential" 方式执行此操作(在开始新操作之前等待以前的未来操作完成),您需要使用来自 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)))
这是遍历List"applicatively"(意思是:并行)
使用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)