2013-09-23 158 views
35

我试图让我的思绪围绕斯卡拉的承诺和未来的构想。需要澄清Scala中的期货和承诺

我一直在阅读Scala文档中的Futures and Promises,我有点困惑,因为我有一种感觉,承诺和期货的概念混在一起。

在我的理解中,承诺是一个容器,我们可以在稍后的时间填充 值。未来是某种不同步的操作,它将在不同的执行路径中完成。

在Scala中,我们可以使用附加的回调函数获取结果。

我失去的是承诺有未来吗?

我也在Clojure上看过这些概念,假设承诺和未来有一些通用的共同概念,但看起来我错了。

承诺p完成p.future返回的未来。这个未来是 特定于承诺p。根据实施情况,它可能是 的情况下,p.future eq页。

val p = promise[T] 
val f = p.future 
+1

在clojure中,Future是一个在单独的线程中执行的函数,如果可用,可以引用一个句柄来访问返回值; clojure中的一个承诺是一个占位符,一旦某个值已经传递给它,它将无法取得变量。我不认为这些与scala有很大关系。 – noisesmith

+1

因此,在编程语言中没有对未来和承诺构造进行一致的概念定义。 – nish1013

+1

也许这将有助于http://stackoverflow.com/questions/13381134/what-are-use-cases-of-scala-promise/13388618#13388618 –

回答

61

你可以认为期货和承诺作为管的两个不同的侧面。 在承诺方面,数据被推入,而在未来方面,数据可以被抽出。

而未来是某种异步操作,它将在不同的执行路径中完成。

事实上,未来是可能在某个时间点变得可用,异步值的占位符对象。它不是异步计算本身。

,有一个叫future未来的构造函数,返回这样的占位符对象滋生是完成此占位符对象并不意味着异步计算被称为未来异步计算的事实。还有其他future constructors/factory methods

但是我不明白的是,承诺有未来吗?

将承诺和期货划分为2个独立的界面是一个设计决策。你可以在同一个界面Future下有这两个,但是这样可以让期货客户完成它们,而不是预期的未来完成者。这会造成意想不到的错误,因为可能有任何数量的争用完成者。

E.g.对于由future构造产生的异步计算,将不再清楚它是否必须完成承诺,或者如果客户端会这样做。

期货和承诺的目的是限制数据在程序中的流动。 这个想法是有一个未来的客户端订阅数据,以便在数据到达时对其执行操作。 承诺客户端的作用是提供这些数据。 混合这两种角色可能会导致程序难以理解或推理。

您可能还会问为什么Promise特征不延伸Future。这是另一个阻止程序员盲目地将Promise传递给客户端的设计决策,他们应该将Promise转换为Future(这种上传很容易被忽略,而必须明确地调用future才能确保每次调用它)。换句话说,通过回复承诺,您有权将其完成给其他人,并且通过返回未来,您有权订阅它。

编辑:

如果您想了解更多关于期货,第4章在斯卡拉书学习并行编程描述它们的细节。免责声明:我是本书的作者。

+0

所以未来是一个容器,最终将在未来获得价值,承诺通过提供价值实现承诺?未来可以通过履行承诺来完成? – nish1013

+2

是 - promise是可以放入值(或错误)的对象。一旦承诺完成,未来对应于特定的承诺就会完成。即使是用于异步计算的'未来'构造也有隐藏的承诺 - 但客户永远不会认为它阻止他完成它。 – axel22

+0

完美!非常感谢这个澄清。最后一点,未来不一定要在异步环境中始终使用,也承诺?即使这种用法也没有什么用处。只是想确保承诺/未来的概念与同步/异步概念无关,即使实际上它们在异步环境中使用时也会非常有用。 – nish1013

14

两者之间的区别在于期货通常以计算为中心,而承诺以数据为中心。

看来你的理解相符这一点,但让我解释一下我的意思是:

在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)完成。在斯卡拉阅读是通过Future特性提供的方法完成的。为了读取承诺的结果,我们必须获得一个实现Future的对象,这是通过p.future完成的。现在如果特征FuturePromise执行,则p.future可以返回this并且两者相等。这纯粹是一种实现选择,不会改变概念。 所以你没有错! 在任何情况下,期货主要是使用回调进行处理。

在这一点或许值得重新考虑的两个概念的初步鉴定:

期货代表了计算在某个时刻会产生一个结果。让我们看看一个可能的实现:我们在某个线程(池)中运行代码,一旦完成,我们安排使用返回值来履行承诺。因此,读未来的结果就是读一个承诺;这是clojure的思维方式(不一定是实施)。

另一方面,一个承诺代表一个价值,将在某个时刻填补。当它被填充时,这意味着一些计算产生了结果。所以从某种意义上说,这就像未来的完成,所以我们应该以相同的方式使用回调来消费价值;这是斯卡拉的思维方式。

4

需要注意的是引擎盖下FuturePromise方面实现这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) } 
    } 
} } 

所以你看,即使他们是独立的概念,你可以独立使用在现实中,你不能得到Future没有使用Promise