为什么 Scala 的 Future 没有 .get / get(maxDuration) 方法,迫使我们转而求助于 Await.result() ?

Why doesn't Scala's Future have a .get / get(maxDuration) method, forcing us to resort to Await.result() instead?

get 方法与 Future class(我希望它驻留的位置)分离并强制编码器必须知道这个叫做 Await?

的外部两种方法 class

Is there any particular advantage in decoupling the get method from the Future class

是的,让开发人员很难做错事Future 表示将在未来完成并且在当前调用点可能不可用的计算。如果你需要阻止未来,为什么不同步执行呢?把它调度到线程池上有什么意义,浪费了一个非常好的线程池线程?

documentation says:

Blocking outside the Future

As mentioned earlier, blocking on a future is strongly discouraged for the sake of performance and for the prevention of deadlocks. Callbacks and combinators on futures are a preferred way to use their results. However, blocking may be necessary in certain situations and is supported by the Futures and Promises API.

甚至 Await object documentation:

While occasionally useful, e.g. for testing, it is recommended that you avoid Await when possible in favor of callbacks and combinators like onComplete and use in for comprehensions. Await will block the thread on which it runs, and could cause performance and deadlock issues.

可以看出语言设计者是有意想要这种效果的。

开始期货

自从 Scala 标准库中的第一个实现以来,就提供了关于 Futures 的 .get。 虽然它有点隐藏:.value.get

scala> val f = Future("test")
f: scala.concurrent.Future[String] = Success(test)

scala> f.value.get
res5: scala.util.Try[String] = Success(test)

虽然使用它的缺点是如果 future 尚未返回,它会抛出异常。

scala> Future("test").value.get
res7: scala.util.Try[String] = Success(test)

scala> Future("test").value.get
 java.util.NoSuchElementException: None.get
 at scala.None$.get(Option.scala:347)
 at scala.None$.get(Option.scala:345)
 ... 32 elided

为什么 Await.result 而不是 get(timeToWait)

添加到@YuvalsItzchakov 的出色回答: 使用 Future i 使异步性在类型系统中可见的想法,所有编码人员都表明使用此概念适用特殊规则,需要谨慎行事。 由于它是一个 Applicative Functor,因此您可以通过使用 mapforeach 以及将它们与 map 和 [=20= 链接起来的方式来使用真正直接的方法来处理这些值].

唯一没有内置的是如何 "unfuture" Future 同时保持特殊时间相关的可配置等待规则。 这将粗略地描述 Await.result 会做什么。

要提供更多有关已接受答案的历史记录1

当我们坐下来正式化 Scala 生态系统中过多的 Future 实现时,我们想确保2 我们没有标准化已知会产生问题的方法。

A Future represents a value (or the inability to produce said value) divorced from time.

更具体的意思是 Future[T] 比简单的 T 具有更少的信息,如下所示:

//At the line of this comment, there is no `f`
val f: Future[T] = … //At the line of this comment, there is an `f` but not necessarily its value of type `T`.

鉴于:

//At the line of this comment, there is no `t`
val t: T = … //At the line of this comment, there is a `T`

为什么这很重要,我听你说。 好吧,如果你想要一个从 Future[T]T 的方法,你是在说:我收到一个可能不可用的值,然后我将 return。

这意味着该方法必须在值尚不可用时»传递时间«,这通常需要阻塞当前执行线程,并且由于执行线程是一种昂贵的资源,进入阻塞状态并再次返回通常需要时间3,这是要避免的。

似乎这还不够,很容易陷入 Futures 无法完成的死锁,因为它们无法获得线程来执行,因为所有这些线程都被卡在尝试 得到 Future 的值。

此外,我们意识到,每当 Future 实现有一个“阻塞获取”操作时,许多用户会调用它而没有意识到它会对他们的程序产生什么影响,直到它在生产中造成问题,因为它看似简单打电话,但直到为时已晚才看到它的成本。

所以我们决定推广不需要阻塞的编程风格,例如回调4和转换5以降低风险死锁和性能不佳。

我们认为,如果我们要完全忽略阻止从 Future 获得结果的能力,最终结果将是每个人都会重新发明它,而且可能质量各不相同。

我们还发现,如果我们将等待的能力设为具体 thing,我们将获得以下好处:

  • 所有 Awaitable 的 API 看起来都一样
  • 代码审查很容易for/against
  • 使用工具 forbid/allow 很容易
  • 我们可以挂钩 blocking 的概念来尝试执行规避操作以减轻死锁,禁止 在运行时,记录它,计时等。

好了!

干杯, √

1 作为 SIP-14 的共同创建者 "Futures and Promises" 并自此维护者。

2 当涉及到 APIs 时,您可以放心……

3 线程唤醒延迟可以有效地将处理限制在 1k-10kops/s 之间。

4 我们也越来越不强调回调的使用,而是提倡使用转换组合器,参见 this 我为 Scala 2.12 写的博客系列期货。

5 例如。 mapflatMapfilterrecoverrecoverWith,以及最近的 transformtransformWith