澄清需要关于Scala的期货和承诺
我试图让我的头脑围绕斯卡拉的承诺和未来的构想。
我一直在阅读Scala文档中的期货和承诺,并且有点困惑,因为我有一种感觉,承诺和期货的概念混杂在一起。
根据我的理解,承诺是一个容器,我们可以在稍后的时间内填充价值。 未来是某种异步操作,可以在不同的执行路径中完成。
在Scala中,我们可以使用未来的附加回调来获得结果。
我失去的是承诺有未来吗?
我也曾在Clojure上看过这些概念,假设承诺和未来有一些通用的概念,但看起来我错了。
承诺p完成p.future返回的未来。 这个未来是特定于承诺p。 根据实施情况,可能是p.future eq p。
val p = promise[T]
val f = p.future
您可以将期货和承诺视为管道的两个不同方面。 在承诺方面,数据被推入,而未来方面,数据可以被抽出。
未来是某种异步操作,可以在不同的执行路径中完成。
实际上,未来是一个占位符对象,可以在某个时间点异步地获得某个值。 它不是异步计算本身。
事实上,有一个名为future
的未来构造函数返回这样的占位符对象,并生成完成此占位符对象的异步计算并不意味着异步计算被称为未来。 还有其他未来的建设者/工厂方法。
但我没有得到的是,诺言有未来吗?
将承诺和期货划分为2个独立的界面是设计决策。 你可以在Future
的同一个接口下使用这两个接口,但是这样可以让期货客户完成它们,而不是完成未来的预期完成。 这会造成意想不到的错误,因为可能有任何数量的争用完成者。
例如,对于由future
结构产生的异步计算,它将不再清楚它是否必须完成承诺,或者客户端是否会这样做。
期货和承诺旨在限制程序中的数据流。 这个想法是让未来的客户订阅数据以便在数据到达时对其执行操作。 承诺客户端的作用是提供这些数据。 混合这两种角色可能会导致难以理解或推理的程序。
你也许会问,为什么Promise
特性不能延伸Future
。 这是阻止程序员盲目地将Promise
传递给客户的另一个设计决策,他们应该将Promise
to the Future
(这个Upcast很容易被忽略,而不得不明确地调用future
的承诺保证你每次调用它)。 换句话说,通过回复承诺,您有权将其完成给其他人,并且通过返回未来,您有权订阅它。
编辑:
如果您想了解更多有关未来的信息,请参阅Scala书籍的并发编程第4章详细介绍它们。 免责声明:我是本书的作者。
两者之间的区别在于期货通常以计算为中心,而承诺以数据为中心。
看起来你的理解与此相符,但让我解释一下我的意思:
在scala和clojure中, 期货 (除非由其他函数/方法返回)通过一些计算创建:
// scala
future { do_something() }
;; clojure
(future (do-something))
在这两种情况下,未来的“返回值”只有在计算结束后才能被读取(没有阻塞)。 当这种情况通常不在程序员的控制范围内时,计算会在后台的某个线程(池)中执行。
相反,在这两种情况下, 承诺都是最初是空的容器,稍后可以填充(恰好一次):
// scala
val p = promise[Int]
...
p success 10 // or failure Exception()
;; clojure
(def p (promise))
(deliver p 10)
一旦发生这种情况可以阅读。
阅读期货和承诺是通过做deref
Clojure中(和realized?
可以用来检查是否deref
将阻止)。 在scala阅读是通过Future
特质提供的方法完成的。 为了读取承诺的结果,我们必须获得一个实现Future的对象,这是通过p.future
完成的。 现在,如果特质Future
是由Promise
实现的,那么p.future
可以返回this
并且两者是平等的。 这纯粹是一种实现选择,不会改变概念。 所以你没有错! 无论如何,期货主要是使用回调进行处理。
在这一点上,重新考虑这两个概念的初始特征可能是值得的:
期货代表了一个计算结果,会在某个时候产生结果。 让我们看看一个可能的实现:我们在某个线程(池)中运行代码,一旦完成,我们安排使用返回值来履行承诺。 因此,读未来的结果就是读一个承诺; 这是clojure的思维方式(不一定是实施)。
另一方面,一个承诺代表一个价值,在某个时刻将被填补。 当它被填充时,这意味着某些计算产生了结果。 所以从某种意义上说,这就像未来的完成,所以我们应该以相同的方式使用回调来消费价值; 这是斯卡拉的思维方式。
请注意, Future
是根据Promise
实现的,并且这个Promise
与您传递给您Future
的正文完成:
def apply[T](body: =>T): Future[T] = impl.Future(body) //here I have omitted the implicit ExecutorContext
impl.Future是Future
特性的一个实现:
def apply[T](body: =>T)(implicit executor: ExecutionContext): scala.concurrent.Future[T] =
{
val runnable = new PromiseCompletingRunnable(body)
executor.prepare.execute(runnable)
runnable.promise.future
}
PromiseCompletingRunnable
看起来像这样:
class PromiseCompletingRunnable[T](body: => T) extends Runnable {
val promise = new Promise.DefaultPromise[T]()
override def run() = {
promise complete {
try Success(body) catch { case NonFatal(e) => Failure(e) }
}
} }
所以你看,即使他们是独立的概念,你可以在现实中独立使用,但如果没有使用Promise
你就无法获得Future
。
上一篇: Clarification needed about futures and promises in Scala