为什么 Scala Future 运行 映射到 Iterator 时顺序

Why is Scala Future running sequentially when mapped over an Iterator

所以我很困惑为什么用 Futures 映射迭代器会使其顺序 运行。考虑以下代码 -

import org.scalameter._

object IteratorVsListInFutures extends App {
  def slowFunction = Thread.sleep(1000)

  import scala.concurrent.ExecutionContext.Implicits.global
  import scala.concurrent._
  import duration._

  println("Should take approximately 4000 ms or 4 sec")
  println{
    withWarmer(new Warmer.Default) measure {
      List(1,2,3,4).foreach(_ => slowFunction)
    }
  }

  println("Should take approximately 1 second")
  println {
    withWarmer(new Warmer.Default) measure {
      val futures: Seq[Future[Unit]] = List(1,2,3,4).map(_ => Future { slowFunction})
      futures.foreach(x => Await.result(x, 10.seconds))
    }
  }

  println("And how long does this take")
  println {
    withWarmer(new Warmer.Default) measure {
      val futures = List(1,2,3,4).iterator.map(_ => Future { slowFunction})
      futures.foreach(x => Await.result(x, 10.seconds))
    }
  }

}

我得到以下结果 -

Should take approximately 4000 ms or 4 sec
4012.132085 ms
Should take approximately 1 second
1004.997573 ms
And how long does this take
4016.533206 ms

Process finished with exit code 0

第一个benchmark和预测的差不多4秒,第二个benchmark也和预测的差不多1秒(因为future是并行执行的) 令我困惑的是,为什么第三个基准测试也是大约 4 秒?

由于non-strict迭代器的性质,以下

List(1,2,3,4).iterator.map(_ => Future { slowFunction })

评估为 Iterator[Future[Unit]] 这只是对要发生但尚未发生的转换的 描述 。关键是看懂

lazy collections are particularly useful to describe successive transformation operations without evaluating intermediate transformations

让我们稍微重写您的示例以强调中间转换

List(1,2,3,4)
  .iterator
  .map(_ => Future { slowFunction })         // <-- intermediate transformation
  .foreach(x => Await.result(x, 10.seconds))

因此 map(_ => Future { slowFunction }) 是未评估的中间转换,但这是应该启动期货的转换。相反,map 转换与 foreach 转换结合起来作为一个单独的转换执行,就像这样

 List(1,2,3,4)
   .foreach(_ => Await.result(Future { slowFunction }, 10.seconds) )

现在很明显,我们正在等待 Future 完成,然后再继续下一个元素,因此 Future 连续完成。