我有一个服务,它返回一个数据类型(下面为Foo
),其中包含一个id列表,用于下面的第二个服务调用getBar
case class Foo(barIds: List[Int])
case class Bar(id: Int)
val f = Future(List(Foo(List(1, 2)), Foo(List(5, 6))))
def getBar(l: List[Int]) = Future(l.map(Bar(_)))
我需要的是Future[List[Foo,List[Bar]]]
我首先尝试了嵌套的for-comprehension但是
val rr = for {
foos <- f
} yield for {
foo <- foos
bars <- getBar(foo.barIds) // won't work as this is a Future and foos is a list
} yield (foo,bars)
然后我玩了一个映射游戏,(闻起来很可怕):
f.map(
foos => foos.map(foo => (foo, foo.barIds)))
.map(ts => ts.map(t => (t._1, getBar(t._2)))
)
但这给了我一个Future[List[Foo,Future[List[Bar]]]]
应该有办法获得Future[List[Foo,List[Bar]]]
并希望以更清洁的方式获得
这是一个scalafiddle https://scalafiddle.io/sf/P0FRIGs/0
请注意我之后的值是:带Foo的元组和“他们”关联的Bar值列表:
List(
(Foo(List(1, 2)),List(Bar(1), Bar(2))),
(Foo(List(5, 6)),List(Bar(5), Bar(6)))
)
答案 0 :(得分:2)
我不确定你希望你的列表是如何构建的,但是这样的事情能做到这一点吗?
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.util.Success
case class Foo(barIds: List[Int])
case class Bar(id: Int)
val f: Future[List[Foo]] = Future(List(Foo(List(1, 2)), Foo(List(5, 6))))
def getBar(l: List[Int]): Future[List[Bar]] = Future(l.map(Bar(_)))
val bars: Future[List[Bar]] = f.flatMap(x => getBar(x.flatMap(_.barIds)))
val out: Future[List[(Foo, List[Bar])]] = for {
foo <- f
bar <- bars
} yield {
foo.map(x => (x, bar))
}
out andThen {case Success(v)=>println(v) }
// List((Foo(List(1, 2)),List(Bar(1), Bar(2), Bar(5), Bar(6))), (Foo(List(5, 6)),List(Bar(1), Bar(2), Bar(5), Bar(6))))
答案 1 :(得分:2)
我会围绕getBar
创建一个帮助程序包装器方法,它返回传入的foo
,并将其与Future.traverse
结合使用,如下所示:
private def getFooAndBars(foo: Foo): Future[(Foo, List[Bar])] =
getBar(foo.barIds).map(foo -> _)
val res: Future[List[(Foo, List[Bar])]] =
f.flatMap(Future.traverse(_)(getFooAndBars))
Future.traverse
会占用每个foo
,在其上调用getFooAndBars
,并展开列表,以便获得Future[List]
而不是List[Future]
。< / p>
答案 2 :(得分:1)
有时将事物映射到所需的结构化可能会很棘手,在这种情况下,您可以选择标识符,以帮助您以更有意义的方式导航复杂性。你走了。
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
case class Foo(barIds: List[Int])
case class Bar(id: Int)
def getBar(l: List[Int]) = Future(l.map(Bar(_)))
val fooListFuture = Future(List(Foo(List(1, 2)), Foo(List(5, 6))))
// You want to get
//Future[List[(Foo,List[Bar])]]
val yourRequireFuture = fooListFuture.flatMap(fooList => {
Future.sequence(fooList.map(foo =>
getBar(foo.barIds).map(barList => (foo, barList))
))
})
答案 3 :(得分:1)
使用map
的解决方案是正确的,但getBar
也会返回未来,您将获得嵌套的未来结果Future[List[Foo,Future[List[Bar]]]]
。您必须使用Future.sequence
将这些未来合并在一起。
val res: Future[List[(Foo,List[Bar])]] = f.flatMap(foos =>
Future.sequence(
foos.map(foo =>
getBar(foo.barIds).map(foo -> _)
)
)
)
此外,您的for
循环解决方案也有效,但您必须另外解决未来的价值。
val res2: Future[List[(Foo,List[Bar])]] = for {
foos <- f
foo <- foos
fbars <- getBar(foo.barIds) // won't work as this is a Future and foos is a list
bars <- fbars //because fbars is future, you have to resolve its value. bars is resolved future value.
} yield (foo,bars)