依赖期货
Dependent futures
开始玩 Scala futures,我被依赖的 futures 困住了。
让我们举个例子。我搜索地点并获得 Future[Seq[Place]]
。对于这些地方中的每一个,我搜索最近的地铁站(服务返回 Future[List[Station]]
)。
我会这样写:
Place.get()
.map { places =>
places.map { place =>
Station.closestFrom(place).map { stations =>
SearchResult(place, stations)
}
}
}
那东西会让我得到一个 Future[Seq[Future[SearchResult]]]
... 这不是我所期望的。
我错过了什么才能得到 Future[Seq[SearchResult]]
?
谢谢大家,
阿尔班
您的解决方案中缺少两个 Future
概念:flatMap
和 Future.sequence
对每个进行解释:
flatMap
与 map
类似,只不过不是给它一个来自 future.map(A => B)
的函数,而是给它一个来自 future.flatMap(A => Future[B])
的函数。这样您就可以将 Futures 链接在一起。
Future.sequence
是一个辅助函数,它将未来列表与列表的未来结合起来:Seq[Future[A]] => Future[Seq[A]]
使用 Future
API 的这两个功能,我们可以将您的答案更改为:
Place.get().flatMap { places =>
Future.sequence(places.map { place =>
Station.closestFrom(place).map { stations =>
SearchResult(place, stations)
}
})
}
简短版
通常使用 for-comprehension 比直接使用 futures 更容易 map/flatMap。在您的情况下,它应该如下所示:
for {places <- Place.get()
searchResults <- Future.traverse(places)(place => for (stations <- Station.closestFrom(place))
yield SearchResult(place,stations)
)
} yield searchResults
详细版本
Future
作为 monad,它为您提供了多种链接操作的方法。
- 如果您想将 'regular' 函数
f : A => B
应用于方框 myfuture : Future[A]
中的内容,确实 map
是获得 Future[B]
的方法.但是在目前的情况下 Station.closestFrom
a 不会给你 List[Stattion]
而是 Future[List[Station]]
.
- 如果你想应用一个单子操作
h : A => Future[B]
或链接其中的几个(这里 Places.get
和 Station.closestFrom
),flatMap
是可行的方法。将 h
应用于 Future[A]
得到 Future[B]
.
- 如果要将单子操作
h : A => Future[B]
应用于 places : Seq[A]
之类的集合,则应使用 Future.traverse : Seq[A] => (A => Future[B]) => Future[Seq[B]]
.
此外,Scala 的 for-compresentation 只是 flatMap/map 的语法糖,因此您可以使用干净清晰的 for 循环,而不是直接使用它们编写复杂的代码。循环:
for { variable1 <- f1
variable2 <- f2
} yield expression
等同于(没有优化):
f1.flatMap( variable1 => f2.map(variable2 => expression))
不要犹豫,使用 for-comprehension,它真的很有帮助。
开始玩 Scala futures,我被依赖的 futures 困住了。
让我们举个例子。我搜索地点并获得 Future[Seq[Place]]
。对于这些地方中的每一个,我搜索最近的地铁站(服务返回 Future[List[Station]]
)。
我会这样写:
Place.get()
.map { places =>
places.map { place =>
Station.closestFrom(place).map { stations =>
SearchResult(place, stations)
}
}
}
那东西会让我得到一个 Future[Seq[Future[SearchResult]]]
... 这不是我所期望的。
我错过了什么才能得到 Future[Seq[SearchResult]]
?
谢谢大家,
阿尔班
您的解决方案中缺少两个 Future
概念:flatMap
和 Future.sequence
对每个进行解释:
flatMap
与 map
类似,只不过不是给它一个来自 future.map(A => B)
的函数,而是给它一个来自 future.flatMap(A => Future[B])
的函数。这样您就可以将 Futures 链接在一起。
Future.sequence
是一个辅助函数,它将未来列表与列表的未来结合起来:Seq[Future[A]] => Future[Seq[A]]
使用 Future
API 的这两个功能,我们可以将您的答案更改为:
Place.get().flatMap { places =>
Future.sequence(places.map { place =>
Station.closestFrom(place).map { stations =>
SearchResult(place, stations)
}
})
}
简短版
通常使用 for-comprehension 比直接使用 futures 更容易 map/flatMap。在您的情况下,它应该如下所示:
for {places <- Place.get()
searchResults <- Future.traverse(places)(place => for (stations <- Station.closestFrom(place))
yield SearchResult(place,stations)
)
} yield searchResults
详细版本
Future
作为 monad,它为您提供了多种链接操作的方法。
- 如果您想将 'regular' 函数
f : A => B
应用于方框myfuture : Future[A]
中的内容,确实map
是获得Future[B]
的方法.但是在目前的情况下Station.closestFrom
a 不会给你List[Stattion]
而是Future[List[Station]]
. - 如果你想应用一个单子操作
h : A => Future[B]
或链接其中的几个(这里Places.get
和Station.closestFrom
),flatMap
是可行的方法。将h
应用于Future[A]
得到Future[B]
. - 如果要将单子操作
h : A => Future[B]
应用于places : Seq[A]
之类的集合,则应使用Future.traverse : Seq[A] => (A => Future[B]) => Future[Seq[B]]
.
此外,Scala 的 for-compresentation 只是 flatMap/map 的语法糖,因此您可以使用干净清晰的 for 循环,而不是直接使用它们编写复杂的代码。循环:
for { variable1 <- f1
variable2 <- f2
} yield expression
等同于(没有优化):
f1.flatMap( variable1 => f2.map(variable2 => expression))
不要犹豫,使用 for-comprehension,它真的很有帮助。