scala.concurrent.Promiseのユースケースは何ですか?


93

私はSIP-14を読んでおり、その概念Futureは完全に理解でき、理解しやすくなっています。ただし、次の2つの質問がありPromiseます。

  1. SIPは言うDepending on the implementation, it may be the case that p.future == p。どうすればいいの?あるFuturePromise2つのタイプがありませんか?

  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()は、同じスレッドでプロデュースSomething()の後に評価します。
セニア2012年

1
質問1に答えるFuturePromiseは、はい、2つの異なるタイプがありますが、github.com / scala / scala / blob / master / src / library / scala / concurrent /からわかるように、この特定のPromise実装も拡張さFutureれます。
ディラン

回答:


118

約束と未来は補完的な概念です。Futureは、将来いつか取得される値であり、そのイベントが発生したときに、それを使って何かを行うことができます。したがって、それは計算の読み取りまたは出力のエンドポイントです-値を取得するものです。

約束は、類推により、計算の書き込み側です。計算の結果を置く場所であるプロミスを作成し、そのプロミスから、プロミスに入れられた結果を読み取るために使用されるフューチャーを取得します。失敗または成功によってプロミスを完了すると、関連するフューチャーに関連付けられていたすべての動作がトリガーされます。

あなたの最初の質問については、どのようにそれは約束のpに対して、我々が持っていることができますp.future == p。これは単一アイテムバッファのように想像できます。最初は空のコンテナで、あとで1つの値を格納して、そのコンテンツが永久に変わるようにすることができます。さて、あなたの視点に応じて、これは約束と未来の両方です。バッファに値を書き込もうとする人にとっては約束です。その値がバッファーに入れられるのを待つ人にとっては未来です。

具体的には、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、ここで

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
  }
}

ご覧のとおり、プロデューサーブロックから得られる結果は、約束に注がれます。

後で編集

実世界での使用について:ほとんどの場合、約束を直接扱うことはありません。非同期計算を実行するライブラリを使用する場合は、ライブラリのメソッドによって返されるフューチャーを操作するだけです。この場合、Promiseはライブラリによって作成されます。これらのメソッドの機能の読み取り側で作業しているだけです。

ただし、独自の非同期APIを実装する必要がある場合は、それらの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 sの使用例はPromise、実装コードに含める必要があります。Futureクライアントコードに公開できる、読み取り専用の優れた機能です。また、Future.future{...}構文が面倒な場合もあります。
ディラン

11
あなたはこのようにそれを見ることができます:あなたは約束なしで未来を持つことはできません。そもそも完成する約束がない場合、フューチャーは値を返すことができません。約束はオプションではなく、将来の必須の書き込み面です。戻り値を提供する人がいないため、futureだけで作業することはできません。
Marius Danila

4
私はあなたが実際の世界で使用している意味を理解していると思います。
Marius Danila

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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.