software - Se necesita aclaración sobre los futuros y las promesas en Scala
scala vs java (3)
La diferencia entre los dos es que los futuros generalmente se centran en el cálculo, mientras que las promesas se centran en los datos.
Parece que su comprensión coincide con esto, pero permítanme explicar lo que quiero decir:
Tanto en Scala como en Clojure, los futuros son (a menos que sean devueltos por alguna otra función / método) creados con algún cálculo:
// scala
future { do_something() }
;; clojure
(future (do-something))
En ambos casos, el "valor de retorno" del futuro solo se puede leer (sin bloqueo) solo después de que el cálculo ha finalizado. Cuando este es el caso, normalmente está fuera del control del programador, ya que el cálculo se ejecuta en algún subproceso (grupo) en el fondo.
Por el contrario, en ambos casos, las promesas son un contenedor inicialmente vacío, que luego puede llenarse (exactamente una vez):
// scala
val p = promise[Int]
...
p success 10 // or failure Exception()
;; clojure
(def p (promise))
(deliver p 10)
Una vez que este es el caso, se puede leer.
Leer los futuros y las promesas se hace a través de deref
in clojure (y se puede deref
que se puede usar para verificar si deref
bloqueará). En scala, la lectura se realiza a través de los métodos proporcionados por el rasgo Future
. Para leer el resultado de una promesa, tenemos que obtener un objeto implementando Futuro, esto lo hace p.future
. Ahora, si el Rasgo Future
se implementa mediante una Promise
, entonces p.future
puede devolver this
y los dos son iguales. Esto es puramente una elección de implementación y no cambia los conceptos. ¡Entonces no estabas equivocado! En cualquier caso, los futuros se tratan principalmente con el uso de devoluciones de llamada.
En este punto, podría valer la pena reconsiderar la caracterización inicial de los dos conceptos:
Los futuros representan un cálculo que producirá un resultado en algún momento. Veamos una implementación posible: ejecutamos el código en algún subproceso (grupo) y una vez hecho, ordenamos usar el valor de retorno para cumplir una promesa. Entonces, leer el resultado del futuro es leer una promesa; Esta es la forma de pensar de Clojure (no necesariamente de implementación).
Por otro lado, una promesa representa un valor que se completará en algún momento. Cuando se llena esto significa que algún cálculo produjo un resultado . Entonces, de alguna manera, esto es como un futuro que se completa, por lo que debemos consumir el valor de la misma manera, utilizando devoluciones de llamada; Esta es la forma de pensar de Scala.
Estoy tratando de entender las promesas de Scala y las construcciones futuras.
He estado leyendo Futures and Promises en Scala Documentation y estoy un poco confundido ya que tengo la sensación de que los conceptos de promesas y futuros están mezclados.
En mi entender, una promesa es un contenedor que podríamos llenar de valor en un momento posterior. Y el futuro es una especie de operación asincrónica que se completará en una ruta de ejecución diferente.
En Scala podemos obtener un resultado utilizando las devoluciones de llamada adjuntas para el futuro.
Donde estoy perdido, ¿cómo promete un futuro?
También he leído sobre estos conceptos en Clojure, suponiendo que la promesa y el futuro tienen algún concepto común genérico, pero parece que estaba equivocado.
Una promesa p completa el futuro devuelto por p.future. Este futuro es específico de la promesa p. Dependiendo de la implementación, puede ser el caso que p.future eq p.
val p = promise[T]
val f = p.future
Puedes pensar en futuros y promesas como dos lados diferentes de una tubería. Por el lado de la promesa, se introducen los datos y, en el lado futuro, se pueden extraer los datos.
Y el futuro es una especie de operación asincrónica que se completará en una ruta de ejecución diferente.
En realidad, un futuro es un objeto marcador de posición para un valor que puede estar disponible en algún momento, de forma asincrónica. No es el cálculo asincrónico en sí mismo.
El hecho de que exista un constructor futuro llamado future
que devuelva dicho objeto marcador de posición y genere un cálculo asincrónico que complete este objeto marcador de posición no significa que el cálculo asincrónico se denomine futuro . También hay otros futuros constructores / métodos de fábrica .
Pero el punto que no entiendo es ¿cómo promete un futuro?
Dividir promesas y futuros en 2 interfaces separadas fue una decisión de diseño. Podría tener estos dos bajo la misma interfaz Future
, pero eso luego les permitiría a los clientes de futuros completarlos en lugar del futuro destinatario del futuro. Esto causaría errores inesperados, ya que podría haber cualquier número de finalizadores contendientes.
Por ejemplo, para el cálculo asincrónico generado por el future
constructo, ya no estaría claro si tiene que completar la promesa, o si el cliente lo hará.
Los futuros y las promesas están destinadas a limitar el flujo de datos en el programa. La idea es tener un cliente futuro que se suscriba a los datos para actuar una vez que los datos lleguen. El papel del cliente promesa es proporcionar esa información. Mezclar estos dos roles puede llevar a programas que son más difíciles de entender o de los que se puede razonar.
También podría preguntar por qué el rasgo de Promise
no se extiende al Future
. Esta es otra decisión de diseño para disuadir a los programadores de pasar ciegamente promesas a clientes en los que deberían actualizar Promise
para el Future
(esta actualización es propensa a quedarse atrás, mientras que tener que llamar explícitamente a future
en la promesa asegura que lo llames siempre). En otras palabras, al devolver una promesa, le está dando el derecho de completarla a otra persona, y al devolverle el futuro le está dando el derecho de suscribirse.
EDITAR:
Si desea obtener más información sobre futuros, el Capítulo 4 del Programa de Aprendizaje Simultáneo en Scala los describe en detalle. Descargo de responsabilidad: soy el autor del libro.
Tenga en cuenta que bajo el capó Future
se implementa en términos de Promise
y esta Promise
se completa con el cuerpo que pasó a su Future
:
def apply[T](body: =>T): Future[T] = impl.Future(body) //here I have omitted the implicit ExecutorContext
impl.Future es una implementación de Rasgo 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
}
Donde PromiseCompletingRunnable
ve así:
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) }
}
} }
Así que, aunque se trate de conceptos independientes que se pueden utilizar de forma independiente, en realidad no se puede obtener el Future
sin utilizar Promise
.