[英]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.