【问题标题】:Unexpected Future.map() execution order意外的 Future.map() 执行顺序
【发布时间】:2016-02-29 05:07:48
【问题描述】:

我有以下 Scala 程序:

object FutureMapTest extends App {
   println("start")

   val f: Future[Long] = Future {
     Thread.sleep(2000)
     val x = 1
     println(s"started with ${x}")
     x
   }
   f.map { i =>
     println(s"mapped to ${i*2}")
   }
   f.map {
     val nothing = "nothing"
     println(s"mapped to ${nothing}")
     _ * 2
   }

   Thread.sleep(3000)
   println("end")
}

我希望它在控制台上打印的是

start
started with 1

后跟(以任何顺序):

mapped to 2
mapped to nothing

紧随其后

end

它实际打印的是:

start
mapped to nothing
started with 1
mapped to 2
end

因此,似乎第二个“map”块会立即执行,而无需等待原始 future 完成。这怎么可能?

你甚至可以从原来的 future 块中删除 Thread.sleep(),结果还是一样的。

【问题讨论】:

    标签: scala


    【解决方案1】:

    这里有几个混淆来源。

    这个:

    f.map {
      val nothing = "nothing"
      println(s"mapped to ${nothing}")
      _ * 2
    }
    

    扩展到:

    f.map {
      val nothing = "nothing"
      println(s"mapped to ${nothing}")
      i => i * 2
    }
    

    这是什么意思?对于某些 Future[A]Future#map 需要 A => B 的函数参数。表达式:

    val nothing = "nothing"
    println(s"mapped to ${nothing}")
    i => i * 2
    

    ..计算为Long => Long,但val 赋值和println 被计算首先,因为它们是返回函数的表达式的一部分。 i => i * 2 直到 f 完成后才会执行。这类似于 (Scala puzzler 001):

    scala> List(1, 2, 3) map {
         |    val a = 1 // this only happens once, not three times
         |    i => a + i + 1
         | }
    res0: List[Int] = List(3, 4, 5)
    

    将其更改为此将表现出您所期望的行为(现在 val 赋值和 println 函数体的一部分):

    f.map { i =>
      val nothing = "nothing"
      println(s"mapped to ${nothing}")
      i * 2
    }
    

    这是另一种看待它的方式:

    f.map {
      println("evaluated immediately")
      i => { println("evaluated after f"); i * 2 }
    }
    

    【讨论】:

    • 另一个困惑是,在没有更多保证的情况下,“结束”必须排在最后。
    猜你喜欢
    • 1970-01-01
    • 2016-03-05
    • 1970-01-01
    • 2012-06-27
    • 1970-01-01
    • 2020-10-20
    • 2017-09-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多