Das Versprechen und die Zukunft sind komplementäre Konzepte. Die Zukunft ist ein Wert, der irgendwann in der Zukunft abgerufen wird, und Sie können damit etwas anfangen, wenn dieses Ereignis eintritt. Es ist daher der ausgelesene oder ausgelesene Endpunkt einer Berechnung - es ist etwas, von dem Sie einen Wert abrufen.
Ein Versprechen ist analog die Schreibseite der Berechnung. Sie erstellen ein Versprechen, an dem Sie das Ergebnis der Berechnung ablegen, und aus diesem Versprechen erhalten Sie eine Zukunft, in der Sie das Ergebnis lesen können, das in das Versprechen aufgenommen wurde. Wenn Sie ein Versprechen entweder durch Misserfolg oder Erfolg erfüllen, lösen Sie das gesamte Verhalten aus, das mit der zugehörigen Zukunft verbunden war.
In Bezug auf Ihre erste Frage, wie kann es sein, dass wir ein Versprechen haben p.future == p
? Sie können sich dies wie einen Einzelelementpuffer vorstellen - einen Container, der anfangs leer ist, und Sie können Nachwörter einen Wert speichern, der für immer zum Inhalt wird. Abhängig von Ihrer Sichtweise ist dies nun sowohl ein Versprechen als auch eine Zukunft. Es ist ein Versprechen für jemanden, der beabsichtigt, den Wert in den Puffer zu schreiben. Es ist eine Zukunft für jemanden, der darauf wartet, dass dieser Wert in den Puffer gestellt wird.
Insbesondere für die gleichzeitige Scala-API können Sie anhand der Promise-Eigenschaft hier sehen, wie die Methoden aus dem Promise-Begleitobjekt implementiert werden:
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))
}
Die Umsetzung der Versprechen DefaultPromise und KeptPromise finden Sie hier . Beide erweitern ein kleines Basismerkmal, das zufällig denselben Namen hat, sich jedoch in einem anderen Paket befindet:
private[concurrent] trait Promise[T] extends scala.concurrent.Promise[T] with scala.concurrent.Future[T] {
def future: this.type = this
}
So können Sie sehen, was sie bedeuten p.future == p
.
DefaultPromise
ist der Puffer, auf den ich mich oben bezogen habe, während KeptPromise
es sich um einen Puffer handelt, dessen Wert von Anfang an eingegeben wurde.
In Bezug auf Ihr Beispiel schafft der zukünftige Block, den Sie dort verwenden, tatsächlich ein Versprechen hinter den Kulissen. Schauen wir uns die Definition der future
in hier :
def future[T](body: =>T)(implicit execctx: ExecutionContext): Future[T] = Future[T](body)
Wenn Sie der Methodenkette folgen, gelangen Sie in die 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
}
}
Wie Sie sehen, wird das Ergebnis, das Sie von Ihrem Produzentenblock erhalten, in ein Versprechen umgewandelt.
SPÄTER BEARBEITEN :
In Bezug auf die reale Verwendung: Meistens werden Sie nicht direkt mit Versprechungen umgehen. Wenn Sie eine Bibliothek verwenden, die asynchrone Berechnungen durchführt, arbeiten Sie nur mit den von den Methoden der Bibliothek zurückgegebenen Futures. Versprechen werden in diesem Fall von der Bibliothek erstellt - Sie arbeiten nur mit dem Leseende dieser Methoden.
Wenn Sie jedoch Ihre eigene asynchrone API implementieren müssen, müssen Sie mit ihnen arbeiten. Angenommen, Sie müssen einen asynchronen HTTP-Client zusätzlich zu Netty implementieren. Dann sieht Ihr Code ungefähr so aus
def makeHTTPCall(request: Request): Future[Response] = {
val p = Promise[Response]
registerOnCompleteCallback(buffer => {
val response = makeResponse(buffer)
p success response
})
p.future
}