¿Cuál es la diferencia entre un futuro y una promesa?


275

¿Cuál es la diferencia entre Futurey Promise?
Ambos actúan como un marcador de posición para resultados futuros, pero ¿dónde está la principal diferencia?


100
Puede hacer un Promisey depende de usted mantenerlo. Cuando alguien más te hace una promesa, debes esperar para ver si la cumplen en elFuture
Kevin Wright

1
Wikipedia Futuros y promesas
wener

30
Uno de los artículos de Wikipedia menos útiles que he leído
Fulluphigh

Respuestas:


146

Según esta discusión , Promisefinalmente se ha pedido CompletableFuturesu inclusión en Java 8, y su javadoc explica:

Un Futuro que puede completarse explícitamente (estableciendo su valor y estado), y puede usarse como un CompletionStage, apoyando funciones dependientes y acciones que se disparan al finalizar.

También se da un ejemplo en la lista:

f.then((s -> aStringFunction(s)).thenAsync(s -> ...);

Tenga en cuenta que la API final es ligeramente diferente pero permite una ejecución asincrónica similar:

CompletableFuture<String> f = ...;
f.thenApply(this::modifyString).thenAccept(System.out::println);

78
No es tu culpa, Assylias, pero ese extracto de javadoc necesita una reparación seria de un autor técnico decente. En mi quinta lectura, puedo comenzar a apreciar lo que está tratando de decir ... ¡y llegué a esto con una comprensión de los futuros y las promesas ya establecidas!
Remolacha-Remolacha

2
@ Remolacha-Remolacha parece que ya ha sucedido.
herman

1
@herman Gracias. He actualizado el enlace para que apunte a la versión final de javadoc.
Assylias

77
@ Remolacha-Remolacha Deberías consultar el documento del método Excepcionalmente. Sería un poema maravilloso, pero es un fracaso excepcional de la documentación legible.
Fulluphigh

44
Para cualquiera que se pregunte, @Fulluphigh se refiere a esto . Parece que fue eliminado / revisado en Java 8.
Cedric Reichenbach

148

(No estoy completamente contento con las respuestas hasta ahora, así que aquí está mi intento ...)

Creo que el comentario de Kevin Wright ( "Puedes hacer una promesa y depende de ti cumplirla. Cuando alguien más te hace una promesa, debes esperar para ver si la cumplen en el futuro" ) lo resume bastante bien, pero algunos La explicación puede ser útil.

Los futuros y las promesas son conceptos bastante similares, la diferencia es que un futuro es un contenedor de solo lectura para un resultado que aún no existe, mientras que una promesa se puede escribir (normalmente solo una vez). El Java 8 CompletableFuture y el Guava SettableFuture pueden considerarse promesas, porque su valor se puede establecer ("completar"), pero también implementan la interfaz Future, por lo tanto, no hay diferencia para el cliente.

El resultado del futuro será establecido por "alguien más", por el resultado de un cálculo asincrónico. Observe cómo FutureTask , un futuro clásico, debe inicializarse con un Callable o Runnable, no hay un constructor sin argumentos, y Future y FutureTask son de solo lectura desde el exterior (los métodos establecidos de FutureTask están protegidos). El valor se establecerá en el resultado del cálculo desde el interior.

Por otro lado, el resultado de una promesa puede ser establecido por "usted" (o de hecho por cualquiera) en cualquier momento porque tiene un método de establecimiento público. Tanto CompletableFuture como SettableFuture se pueden crear sin ninguna tarea, y su valor se puede establecer en cualquier momento. Envía una promesa al código del cliente y la cumple más tarde como lo desee.

Tenga en cuenta que CompletableFuture no es una promesa "pura", puede inicializarse con una tarea como FutureTask, y su característica más útil es el encadenamiento no relacionado de los pasos de procesamiento.

También tenga en cuenta que una promesa no tiene que ser un subtipo de futuro y no tiene que ser el mismo objeto. En Scala, un objeto Futuro se crea mediante un cálculo asincrónico o mediante un objeto Promise diferente . En C ++, la situación es similar: el objeto de promesa es utilizado por el productor y el futuro por el consumidor. La ventaja de esta separación es que el cliente no puede establecer el valor del futuro.

Tanto Spring como EJB 3.1 tienen una clase AsyncResult, que es similar a las promesas de Scala / C ++. AsyncResult implementa Future pero este no es el futuro real: los métodos asincrónicos en Spring / EJB devuelven un objeto Future diferente de solo lectura a través de cierta magia de fondo, y el cliente puede usar este segundo futuro "real" para acceder al resultado.


116

Soy consciente de que ya hay una respuesta aceptada pero, sin embargo, me gustaría agregar mis dos centavos:

TLDR: Future y Promise son los dos lados de una operación asincrónica: consumidor / llamante versus productor / implementador .

Como llamador de un método API asíncrono, obtendrá Futureun control del resultado del cálculo. Puede, por ejemplo, invocarlo get()para esperar a que se complete el cálculo y recuperar el resultado.

Ahora piense en cómo se implementa este método API: el implementador debe devolver un Futureinmediato. Son responsables de completar ese futuro tan pronto como se realice el cálculo (que sabrán porque está implementando la lógica de despacho ;-)). Utilizarán un Promise/ CompletableFuturepara hacer exactamente eso: construir y devolver el CompletableFutureinmediatamente, y llamar complete(T result)una vez que se haya realizado el cálculo.


1
¿Esto implica que una Promesa es siempre una subclase del Futuro, y que la capacidad de escritura del Futuro está oscurecida por el tipo?
devios1

No creo que esté implícito . En cuanto a la implementación, a menudo será el caso (por ejemplo, en Java, Scala).
Rahel Lüthy

74

Daré un ejemplo de lo que es Promise y cómo se puede establecer su valor en cualquier momento, en oposición a Future, cuyo valor solo es legible.

Supongamos que tienes una madre y le pides dinero.

// Now , you trick your mom into creating you a promise of eventual
// donation, she gives you that promise object, but she is not really
// in rush to fulfill it yet:
Supplier<Integer> momsPurse = ()-> {

        try {
            Thread.sleep(1000);//mom is busy
        } catch (InterruptedException e) {
            ;
        }

        return 100;

    };


ExecutorService ex = Executors.newFixedThreadPool(10);

CompletableFuture<Integer> promise =  
CompletableFuture.supplyAsync(momsPurse, ex);

// You are happy, you run to thank you your mom:
promise.thenAccept(u->System.out.println("Thank you mom for $" + u ));

// But your father interferes and generally aborts mom's plans and 
// completes the promise (sets its value!) with far lesser contribution,
// as fathers do, very resolutely, while mom is slowly opening her purse 
// (remember the Thread.sleep(...)) :
promise.complete(10); 

La salida de eso es:

Thank you mom for $10

La promesa de mamá fue creada, pero esperó algún evento de "finalización".

CompletableFuture<Integer> promise...

Creaste tal evento, aceptando su promesa y anunciando tus planes para agradecer a tu madre:

promise.thenAccept...

En este momento, mamá comenzó a abrir su bolso ... pero muy lento ...

y el padre interfirió mucho más rápido y completó la promesa en lugar de su madre:

promise.complete(10);

¿Has notado un albacea que escribí explícitamente?

Curiosamente, si utiliza un ejecutor implícito predeterminado (commonPool) y el padre no está en casa, pero solo la madre con su "bolso lento", entonces su promesa solo se completará, si el programa dura más de lo que la madre necesita para obtener dinero del bolso.

El ejecutor predeterminado actúa como un "demonio" y no espera a que se cumplan todas las promesas. No he encontrado una buena descripción de este hecho ...


8
¡Es muy divertido leer este! No creo que pueda olvidar el futuro y prometer más.
user1532146

2
Esto debe ser aceptado como la respuesta. Es como leer una historia. Gracias @Vladimir
Phillen

Gracias @Vladimir
intvprep

9

No estoy seguro de si esto puede ser una respuesta, pero como veo lo que otros han dicho sobre alguien, puede parecer que necesita dos abstracciones separadas para ambos conceptos, de modo que uno de ellos ( Future) es solo una vista de solo lectura del otro ( Promise) ... pero en realidad esto no es necesario.

Por ejemplo, eche un vistazo a cómo se definen las promesas en javascript:

https://promisesaplus.com/

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

El enfoque está en la capacidad de compilación utilizando el thenmétodo como:

asyncOp1()
.then(function(op1Result){
  // do something
  return asyncOp2();
})
.then(function(op2Result){
  // do something more
  return asyncOp3();
})
.then(function(op3Result){
  // do something even more
  return syncOp4(op3Result);
})
...
.then(function(result){
  console.log(result);
})
.catch(function(error){
  console.log(error);
})

lo que hace que el cálculo asíncrono parezca síncrono:

try {
  op1Result = syncOp1();
  // do something
  op1Result = syncOp2();
  // do something more
  op3Result = syncOp3();
  // do something even more
  syncOp4(op3Result);
  ...
  console.log(result);
} catch(error) {
  console.log(error);
}

lo cual es genial (No es tan genial como async-await pero async-await simplemente elimina la repetitiva .... luego (función (resultado) {.... de ella).

Y en realidad su abstracción es bastante buena como constructor de promesas

new Promise( function(resolve, reject) { /* do it */ } );

le permite proporcionar dos devoluciones de llamada que se pueden utilizar para completar con Promiseéxito o con un error. De modo que solo el código que construye el Promisepuede completarlo y el código que recibe un Promiseobjeto ya construido tiene la vista de solo lectura.

Con la herencia, se puede lograr lo anterior si resolver y rechazar son métodos protegidos.


44
+1. Esta es la respuesta correcta a esta pregunta. CompletableFuturepuede tener alguna similitud con un Promisepero todavía no es unPromise , porque la forma en que se pretende consumir es diferente: Promiseel resultado de a se consume al llamar then(function), y la función se ejecuta en el contexto del productor inmediatamente después de que el productor llama resolve. El Futureresultado de A se consume al llamar, lo getque hace que el subproceso del consumidor espere hasta que el subproceso del productor haya generado el valor y luego lo procese en el consumidor. Futurees inherentemente multiproceso, pero ...
Periata Breatta 03 de

55
... es completamente posible usar un Promisesolo hilo (y de hecho, ese es el entorno preciso para el que fueron diseñados originalmente: las aplicaciones javascript generalmente solo tienen un solo hilo, por lo que no puede implementarlo Futureallí). Promisees, por lo tanto, mucho más liviano y eficiente que Future, pero Futurepuede ser útil en situaciones que son más complejas y requieren cooperación entre hilos que no se pueden organizar fácilmente usando Promises. Para resumir: Promisees un modelo push, mientras que Futurees un modelo pull (cf Iterable vs Observable)
Periata Breatta

@PeriataBreatta Incluso en un entorno de un solo subproceso, debe haber algo que cumpla la promesa (que generalmente se ejecuta como un hilo diferente, por ejemplo, un XMLHttpRequest). No creo en la afirmación de eficiencia, ¿tienes algunas cifras? +++ Dicho eso, una muy buena explicación.
maaartinus

1
@maaartinus: sí, algo debe cumplir la promesa, pero puede hacerlo (y de hecho en muchos casos) mediante un ciclo de nivel superior que sondea los cambios en el estado externo y resuelve las promesas relacionadas con las acciones que han finalizado. En cuanto a la eficiencia, no tengo cifras firmes para Promesas específicamente, pero tenga en cuenta que recurrir geta una resolución Futureno resuelta necesariamente implicará cambios de contexto de 2 hilos, que al menos unos años atrás probablemente requerirían alrededor de 50 de nosotros .
Periata Breatta

@PeriataBreatta En realidad, su comentario debe ser la solución aceptada. Estaba buscando una explicación (pull / push, single / multi-thread) como la suya.
Thomas Jacob

5

Para el código del cliente, Promise es para observar o adjuntar una devolución de llamada cuando hay un resultado disponible, mientras que Future es esperar el resultado y luego continuar. Teóricamente, cualquier cosa que sea posible hacer con futuros, lo que se puede hacer con promesas, pero debido a la diferencia de estilo, la API resultante para promesas en diferentes idiomas facilita el encadenamiento.


2

No hay un método establecido en la interfaz Future, solo obtiene un método, por lo que es de solo lectura. Sobre CompletableFuture, este artículo puede ser útil. futuro completo

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.