[英]Chaining Scala nested futures
我在 Scala 中链接期货时遇到了一些麻烦。 假设我有以下内容:
def fetchInts(s: String): Future[List[Int]] = Future {
(0 to s.length).toList
}
def fetchChars(a: Int): Future[List[Char]] = Future {
("a" * a).toList
}
对于给定的String
我想获取Int
s,然后对于每个获取的Int
我想获取Char
s,所有这些都尽可能异步。 我想要得到的 output 类型是Future[List[(Int, List[Char])]]
并且在语义上我想做这样的事情:
def fetch(s: String): Future[List[(Int, List[Char])]] = {
for {
as <- fetchInts(s)
a <- as
cs <- fetchChars(a)
} yield (a, cs)
}
当我混合List
和Future
monads 时,上述内容不会进行类型检查,所以到目前为止我想出了:
def fetch(s: String): Future[List[(Int, List[Char])]] = {
val fas: Future[List[Int]] = fetchInts(s)
fas.flatMap { as: List[Int] =>
val res = as.map { a: Int =>
val fcs: Future[List[Char]] = fetchChars(a)
fcs.flatMap(cs => Future((a, cs)))
}
Future.sequence(res)
}
}
这种类型检查但看起来很笨拙,我怀疑这是否是线程最优的,因为我没有完全掌握Futures.sequence()
的语义。 还有其他更简单、更惯用的方法来执行此操作吗?
Scala 中的理解是同一实体上的地图和平面地图。 在您的 fetch 方法中,它超过了期货。 它不起作用的原因as
List[Int]
也是如此,而不是未来。 在这个用例中,无需理解就更容易编写它。 您可以尝试以下方法:
def fetch(s: String): Future[List[(Int, List[Char])]] = {
fetchInts(s).flatMap { ints =>
Future.traverse(ints)(i =>
fetchChars(i).map { chars =>
i -> chars
}
)
}
}
的结果:
println(Await.result(fetch("abrakadabra"), 2.seconds))
是:
List((0,List()), (1,List(a)), (2,List(a, a)), (3,List(a, a, a)), (4,List(a, a, a, a)), (5,List(a, a, a, a, a)), (6,List(a, a, a, a, a, a)), (7,List(a, a, a, a, a, a, a)), (8,List(a, a, a, a, a, a, a, a)), (9,List(a, a, a, a, a, a, a, a, a)), (10,List(a, a, a, a, a, a, a, a, a, a)), (11,List(a, a, a, a, a, a, a, a, a, a, a)))
来自 Scala 文档Future.traverse :
使用提供的 function A => Future[B] 将 IterableOnce[A] 异步且非阻塞地转换为 Future[IterableOnce[B]]。 这对于执行并行 map 很有用。
代码在Scastie运行。
如果您毕竟想用于理解,您可以尝试:
def fetch1(s: String): Future[List[(Int, List[Char])]] = {
for {
ints <- fetchInts(s)
result <- Future.traverse(ints)(i => for {
chars <- fetchChars(i)
} yield i -> chars)
} yield result
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.