La Promesa y el Futuro son conceptos complementarios. El futuro es un valor que se recuperará, bueno, en algún momento en el futuro y puedes hacer cosas con él cuando suceda ese evento. Por lo tanto, es el punto final de lectura o salida de un cálculo; es algo de lo que recupera un valor.
Una promesa es, por analogía, el lado de la escritura del cálculo. Creas una promesa que es el lugar donde colocarás el resultado del cálculo y de esa promesa obtienes un futuro que se utilizará para leer el resultado que se puso en la promesa. Cuando complete una Promesa, ya sea por fracaso o por éxito, activará todo el comportamiento que se adjuntó al Futuro asociado.
Respecto a tu primera pregunta, ¿cómo puede ser que por una promesa p tenemos p.future == p
. Puede imaginar esto como un búfer de un solo elemento: un contenedor que inicialmente está vacío y puede almacenar un valor que se convertirá en su contenido para siempre. Ahora, dependiendo de su punto de vista, esto es tanto una promesa como un futuro. Es una promesa para alguien que tenga la intención de escribir el valor en el búfer. Es un futuro para alguien que espera que ese valor se ponga en el búfer.
Específicamente, para la API concurrente de Scala, si echas un vistazo al rasgo Promise aquí , puedes ver cómo se implementan los métodos del objeto complementario 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))
}
Ahora, las implementaciones de promesas, DefaultPromise y KeptPromise se pueden encontrar aquí . Ambos extienden un pequeño rasgo básico que tiene el mismo nombre, pero se encuentra en un paquete diferente:
private[concurrent] trait Promise[T] extends scala.concurrent.Promise[T] with scala.concurrent.Future[T] {
def future: this.type = this
}
Para que pueda ver lo que quieren decir p.future == p
.
DefaultPromise
es el búfer al que me refería anteriormente, mientras que KeptPromise
es un búfer con el valor puesto desde su creación.
Con respecto a su ejemplo, el bloque futuro que usa allí en realidad crea una promesa detrás de escena. Echemos un vistazo a la definición de future
de aquí :
def future[T](body: =>T)(implicit execctx: ExecutionContext): Future[T] = Future[T](body)
Siguiendo la cadena de métodos, terminas en el 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
}
}
Entonces, como puede ver, el resultado que obtiene de su bloque de productor se vierte en una promesa.
EDICIÓN POSTERIOR :
Con respecto al uso en el mundo real: la mayoría de las veces no se ocupará directamente de las promesas. Si va a utilizar una biblioteca que realiza cálculos asincrónicos, simplemente trabajará con los futuros devueltos por los métodos de la biblioteca. En este caso, las promesas son creadas por la biblioteca; solo está trabajando con el extremo de lectura de lo que hacen esos métodos.
Pero si necesita implementar su propia API asincrónica, tendrá que comenzar a trabajar con ellos. Suponga que necesita implementar un cliente HTTP asíncrono además de, digamos, Netty. Entonces su código se verá algo así
def makeHTTPCall(request: Request): Future[Response] = {
val p = Promise[Response]
registerOnCompleteCallback(buffer => {
val response = makeResponse(buffer)
p success response
})
p.future
}