链接 Scala 嵌套期货

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 我想获取 Ints 然后对于每个获取的 Int 我想获取 Chars,尽可能异步.我想要得到的输出类型是 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)
}

上面不会进行类型检查,因为我混合了 ListFuture monad,所以到目前为止我想出了:

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 中的 for-comprehension,是同一实体上的地图和平面地图。在您的获取方法中,它超过了期货。它不起作用的原因是 asList[Int],而不是未来。在这个用例中,不用 for-comprehension 写起来会更容易。您可以尝试以下方法:

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:

Asynchronously and non-blockingly transforms a IterableOnce[A] into a Future[IterableOnce[B]] using the provided function A => Future[B]. This is useful for performing a parallel 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
}