scala.concurrent.Promise的用例是什么?


93

我正在阅读SIP-14,其概念Future非常合理且易于理解。但是有两个问题Promise

  1. SIP说Depending on the implementation, it may be the case that p.future == p。怎么会这样?是FuturePromise不是两种不同类型的?

  2. 我们Promise什么时候应该使用?示例producer and consumer代码:

    import scala.concurrent.{ future, promise }
    val p = promise[T]
    val f = p.future
    
    val producer = future {
        val r = produceSomething()
        p success r
        continueDoingSomethingUnrelated()
    }
    val consumer = future {
        startDoingSomething()
        f onSuccess {
            case r => doSomethingWithResult()
        }
    }

很容易阅读,但是我们真的需要这样写吗?我试图仅在Future且没有Promise的情况下实现它,如下所示:

val f = future {
   produceSomething()
}

val producer = future {
   continueDoingSomethingUnrelated()
}

startDoingSomething()

val consumer = future {
  f onSuccess {
    case r => doSomethingWithResult()
  }
}

此示例与给定的示例有什么区别,什么使Promise成为必需?


在第一个示例中,continueDoingSomethingUnrelated()在同一线程中的produceSomething()之后求值。
senia 2012年

1
要回答问题1,是的,Future并且Promise是两种不同的类型,但是正如您从github.com/scala/scala/blob/master/src/library/scala/concurrent/中看到的那样,该特定Promise实现也得到了扩展Future
迪伦

Answers:


118

承诺和未来是相辅相成的概念。Future是一个可以在将来的某个时间检索的值,当该事件发生时,您可以使用它进行处理。因此,它是计算的读取或输出端点-您可以从中检索值。

以此类推,Promise是计算的编写方。您创建一个promise,在该位置放置计算结果,然后从该promise中获得一个future,该future将用于读取放入promise中的结果。当您通过失败或成功完成一个Promise时,您将触发关联到Future的所有行为。

关于您的第一个问题,我们有一个诺言p怎么可能呢p.future == p?您可以想象这就像一个单项缓冲区–一个最初为空的容器,您可以在后记中存储一个值,该值将永远成为其内容。现在,根据您的观点,这既是一个承诺,也是一个未来。对于打算将值写入缓冲区的人来说,这是一个承诺。对于等待该值放入缓冲区的人来说,这是一个未来。

具体来说,对于Scala并发API,如果您在此处查看Promise特性,则可以看到Promise伴随对象的方法是如何实现的:

object Promise {

  /** Creates a promise object which can be completed with a value.
   *  
   *  @tparam T       the type of the value in the promise
   *  @return         the newly created `Promise` object
   */
  def apply[T](): Promise[T] = new impl.Promise.DefaultPromise[T]()

  /** Creates an already completed Promise with the specified exception.
   *  
   *  @tparam T       the type of the value in the promise
   *  @return         the newly created `Promise` object
   */
  def failed[T](exception: Throwable): Promise[T] = new impl.Promise.KeptPromise[T](Failure(exception))

  /** Creates an already completed Promise with the specified result.
   *  
   *  @tparam T       the type of the value in the promise
   *  @return         the newly created `Promise` object
   */
  def successful[T](result: T): Promise[T] = new impl.Promise.KeptPromise[T](Success(result))

}

现在,可以在此处找到Promise的那些实现,即DefaultPromise和KeptPromise 。它们都扩展了一个基本的小特征,恰好具有相同的名称,但是它位于不同的包中:

private[concurrent] trait Promise[T] extends scala.concurrent.Promise[T] with scala.concurrent.Future[T] {
  def future: this.type = this
}

这样您就可以了解它们的含义p.future == p

DefaultPromise是上面我所指的缓冲区,而KeptPromise缓冲区是从其创建时就已放入值的缓冲区。

对于您的示例,在那里使用的Future块实际上在幕后创造了希望。让我们看的定义future这里

def future[T](body: =>T)(implicit execctx: ExecutionContext): Future[T] = Future[T](body)

通过遵循方法链,您最终会遇到impl.Future

private[concurrent] object Future {
  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) }
      }
    }
  }

  def apply[T](body: =>T)(implicit executor: ExecutionContext): scala.concurrent.Future[T] = {
    val runnable = new PromiseCompletingRunnable(body)
    executor.execute(runnable)
    runnable.promise.future
  }
}

因此,正如您所看到的,从生产者模块中获得的结果被注入了承诺。

之后编辑

关于实际用途:大多数情况下,您不会直接处理承诺。如果您要使用执行异步计算的库,那么就可以使用该库的方法返回的期货。在这种情况下,承诺是由库创建的-您只是在阅读这些方法的作用。

但是,如果您需要实现自己的异步API,则必须开始使用它们。假设您需要在Netty之上实现一个异步HTTP客户端。然后您的代码将看起来像这样

    def makeHTTPCall(request: Request): Future[Response] = {
        val p = Promise[Response]
        registerOnCompleteCallback(buffer => {
            val response = makeResponse(buffer)
            p success response
        })
        p.future
    }

3
@xiefei的用例Promise应在实现代码中。Future您可以将其公开给客户端代码,这是一件很好的只读操作。另外,Future.future{...}语法有时会很麻烦。
迪伦

11
您可以这样看:没有承诺就没有未来。如果没有一开始就不能兑现的承诺,未来就不能返回值。承诺不是可选的,它们是未来的强制性写作方面。您不能只与期货合作,因为不会有人为他们提供返回值。
Marius Danila

4
我想我明白您在现实世界中的用意:我已经更新了我的回答,以举一个例子。
Marius Danila 2012年

2
@Marius:考虑到给出的真实示例,如果makeHTTPCall这样实现: def makeHTTPCall(request: Request): Future[Response] = { Future { registerOnCompleteCallback(buffer => { val response = makeResponse(buffer) response }) } }
puneetk

1
@puneetk,那么您将拥有未来,它会在完成后立即registerOnCompleteCallback()完成。此外,它不会返回Future[Response]。它返回Future[registerOnCompleteCallback() return type]
Evgeny Veretennikov
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.