简体   繁体   English

链接 Scala 嵌套期货

[英]Chaining Scala nested futures

I have some troubles chaining futures in Scala.我在 Scala 中链接期货时遇到了一些麻烦。 Suppose I have the following:假设我有以下内容:

def fetchInts(s: String): Future[List[Int]] = Future {
  (0 to s.length).toList
}

def fetchChars(a: Int): Future[List[Char]] = Future {
  ("a" * a).toList
}

and for a given String I want to fetch Int s and then for each fetched Int I want to fetch Char s, all as async as possible.对于给定的String我想获取Int s,然后对于每个获取的Int我想获取Char s,所有这些都尽可能异步。 The output type I want to get is Future[List[(Int, List[Char])]] and semantically I want to do something like that:我想要得到的 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)
}

The above will not type-check as I mix List and Future monads so I have so far come up with:当我混合ListFuture 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)
  }
}

This type-checks but looks clumsy and I have some doubts if this is thread-optimal as I do not fully grasp the semantics of Futures.sequence() .这种类型检查但看起来很笨拙,我怀疑这是否是线程最优的,因为我没有完全掌握Futures.sequence()的语义。 Is there some other simpler, idiomatic way to perform this?还有其他更简单、更惯用的方法来执行此操作吗?

for-comprehension in Scala, is maps and flatMaps over the same entity. Scala 中的理解是同一实体上的地图和平面地图。 In your fetch method, it is over futures.在您的 fetch 方法中,它超过了期货。 The reason it doesn't work, is that as is List[Int] , and not a future.它不起作用的原因as List[Int]也是如此,而不是未来。 In this use case, it will be easier to write it without for-comprehension.在这个用例中,无需理解就更容易编写它。 You can try the following:您可以尝试以下方法:

def fetch(s: String): Future[List[(Int, List[Char])]] = {
  fetchInts(s).flatMap { ints =>
    Future.traverse(ints)(i =>
      fetchChars(i).map { chars =>
        i -> chars
      }
    )
  }
}

The result of:的结果:

println(Await.result(fetch("abrakadabra"), 2.seconds))

Is:是:

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)))

From Scala docs Future.traverse :来自 Scala 文档Future.traverse

Asynchronously and non-blockingly transforms a IterableOnce[A] into a Future[IterableOnce[B]] using the provided function A => Future[B].使用提供的 function A => Future[B] 将 IterableOnce[A] 异步且非阻塞地转换为 Future[IterableOnce[B]]。 This is useful for performing a parallel map.这对于执行并行 map 很有用。

Code run at Scastie .代码在Scastie运行。

If after all you do want to use for comprehension, you can try:如果您毕竟想用于理解,您可以尝试:

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM