我有以下情况:
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不等待未来完成。
这个简单用例的解决方案是什么?
答案 0 :(得分:2)
你通常不能在一个for
理解中组合不同的monad(除了scala的集合之外)。在这里,您希望组合Future
和List
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)