Quelle est la différence entre Future
et Promise
?
Ils agissent tous deux comme un espace réservé pour les résultats futurs, mais où est la principale différence?
Quelle est la différence entre Future
et Promise
?
Ils agissent tous deux comme un espace réservé pour les résultats futurs, mais où est la principale différence?
Réponses:
Selon cette discussion , Promise
a finalement été appelé CompletableFuture
à être inclus dans Java 8, et son javadoc explique:
Un Futur qui peut être explicitement achevé (en définissant sa valeur et son statut), et qui peut être utilisé comme CompletionStage, prenant en charge les fonctions et actions dépendantes qui se déclenchent lors de son achèvement.
Un exemple est également donné sur la liste:
f.then((s -> aStringFunction(s)).thenAsync(s -> ...);
Notez que l'API finale est légèrement différente mais permet une exécution asynchrone similaire:
CompletableFuture<String> f = ...;
f.thenApply(this::modifyString).thenAccept(System.out::println);
(Je ne suis pas complètement satisfait des réponses jusqu'à présent, alors voici ma tentative ...)
Je pense que le commentaire de Kevin Wright ( "Vous pouvez faire une promesse et c'est à vous de la tenir. Quand quelqu'un d'autre vous fait une promesse, vous devez attendre de voir s'il l'honorera dans le futur" ) le résume assez bien, mais certains l'explication peut être utile.
Futures et promesses sont des concepts assez similaires, la différence est qu'un futur est un conteneur en lecture seule pour un résultat qui n'existe pas encore, alors qu'une promesse peut être écrite (normalement une seule fois). Le Java 8 CompletableFuture et le Guava SettableFuture peuvent être considérés comme des promesses, car leur valeur peut être définie ("terminée"), mais ils implémentent également l'interface Future, il n'y a donc pas de différence pour le client.
Le résultat du futur sera fixé par "quelqu'un d'autre" - par le résultat d'un calcul asynchrone. Notez comment FutureTask - un futur classique - doit être initialisé avec un Callable ou Runnable, il n'y a pas de constructeur sans argument, et Future et FutureTask sont en lecture seule de l'extérieur (les méthodes définies de FutureTask sont protégées). La valeur sera définie sur le résultat du calcul de l'intérieur.
D'un autre côté, le résultat d'une promesse peut être défini par "vous" (ou en fait par n'importe qui) à tout moment car il a une méthode de définition publique. CompletableFuture et SettableFuture peuvent être créés sans aucune tâche et leur valeur peut être définie à tout moment. Vous envoyez une promesse au code client et la remplissez plus tard comme vous le souhaitez.
Notez que CompletableFuture n'est pas une promesse "pure", il peut être initialisé avec une tâche tout comme FutureTask, et sa fonctionnalité la plus utile est le chaînage indépendant des étapes de traitement.
Notez également qu'une promesse n'a pas à être un sous-type de futur et qu'elle ne doit pas être le même objet. Dans Scala, un objet Future est créé par un calcul asynchrone ou par un autre objet Promise. En C ++ la situation est similaire: l'objet promesse est utilisé par le producteur et l'objet futur par le consommateur. L'avantage de cette séparation est que le client ne peut pas fixer la valeur de l'avenir.
Les deux printemps et EJB 3.1 ont une classe AsyncResult, qui est semblable aux promesses de la / Scala C. AsyncResult implémente Future mais ce n'est pas le vrai futur: les méthodes asynchrones dans Spring / EJB renvoient un objet Future différent et en lecture seule via une magie d'arrière-plan, et ce deuxième futur "réel" peut être utilisé par le client pour accéder au résultat.
Je suis conscient qu'il existe déjà une réponse acceptée, mais je voudrais néanmoins ajouter mes deux cents:
TLDR: Future et Promise sont les deux faces d'une opération asynchrone: consommateur / appelant vs producteur / réalisateur .
En tant qu'appelant d'une méthode d'API asynchrone, vous obtiendrez un en Future
tant que descripteur du résultat du calcul. Vous pouvez par exemple l'appeler get()
pour attendre la fin du calcul et récupérer le résultat.
Pensez maintenant à la façon dont cette méthode API est réellement implémentée: l' implémenteur doit retourner Future
immédiatement un . Ils sont chargés de compléter cet avenir dès que le calcul est terminé (ce qu'ils sauront car il implémente la logique de répartition ;-)). Ils utiliseront a Promise
/ CompletableFuture
pour faire exactement cela: Construire et retourner le CompletableFuture
immédiatement, et appeler complete(T result)
une fois le calcul terminé.
Je vais donner un exemple de ce qu'est Promise et comment sa valeur pourrait être définie à tout moment, contrairement à Future, qui est uniquement lisible.
Supposons que vous ayez une maman et que vous lui demandiez de l'argent.
// 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 sortie de cela est:
Thank you mom for $10
La promesse de maman a été créée, mais a attendu un événement "d'achèvement".
CompletableFuture<Integer> promise...
Vous avez créé un tel événement, en acceptant sa promesse et en annonçant vos plans pour remercier votre maman:
promise.thenAccept...
À ce moment, maman a commencé à ouvrir son sac à main ... mais très lentement ...
et le père est intervenu beaucoup plus rapidement et a rempli la promesse au lieu de votre maman:
promise.complete(10);
Avez-vous remarqué un exécuteur que j'ai écrit explicitement?
Fait intéressant, si vous utilisez un exécuteur implicite implicite à la place (commonPool) et que le père n'est pas à la maison, mais seulement maman avec son "sac à main lent", alors sa promesse ne se terminera que si le programme vit plus longtemps que maman a besoin d'obtenir de l'argent du bourse.
L'exécuteur par défaut agit un peu comme un "démon" et n'attend pas que toutes les promesses soient tenues. Je n'ai pas trouvé une bonne description de ce fait ...
Je ne sais pas si cela peut être une réponse, mais comme je vois ce que les autres ont dit pour quelqu'un, il peut sembler que vous avez besoin de deux abstractions distinctes pour ces deux concepts afin que l'un d'eux ( Future
) soit juste une vue en lecture seule de l'autre ( Promise
) ... mais en fait ce n'est pas nécessaire.
Par exemple, regardez comment les promesses sont définies en javascript:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
L'accent est mis sur la composabilité en utilisant la then
méthode comme:
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);
})
ce qui rend le calcul asynchrone comme synchrone:
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);
}
ce qui est plutôt cool. (Pas aussi cool que async-wait mais async-wait supprime simplement le passe-partout .... puis (fonction (résultat) {.... de celui-ci).
Et en fait, leur abstraction est assez bonne en tant que constructeur de promesses
new Promise( function(resolve, reject) { /* do it */ } );
vous permet de fournir deux rappels qui peuvent être utilisés pour terminer Promise
avec succès ou avec une erreur. Ainsi, seul le code qui construit le Promise
peut le compléter et le code qui reçoit un Promise
objet déjà construit a la vue en lecture seule.
Avec l'héritage, ce qui précède peut être atteint si la résolution et le rejet sont des méthodes protégées.
CompletableFuture
peut avoir une certaine similitude avec a Promise
mais ce n'est toujours pas le casPromise
, car la façon dont il est destiné à être consommé est différente: Promise
le résultat de a est consommé en appelant then(function)
, et la fonction est exécutée dans le contexte du producteur immédiatement après l'appel du producteur resolve
. Le Future
résultat de A est consommé en appelant, get
ce qui oblige le thread consommateur à attendre que le thread producteur ait généré la valeur, puis la traite dans le consommateur. Future
est intrinsèquement multithread, mais ...
Promise
avec un seul thread (et en fait c'est l'environnement précis pour lequel ils ont été initialement conçus: les applications javascript n'ont généralement qu'un seul thread, vous ne pouvez donc pas les implémenter Future
). Promise
est donc beaucoup plus léger et efficace que Future
, mais Future
peut être utile dans des situations plus complexes et nécessitant une coopération entre des threads qui ne peuvent pas être facilement arrangés en utilisant Promise
s. Pour résumer: Promise
est un modèle push, tandis que Future
est un modèle pull (cf Iterable vs Observable)
XMLHttpRequest
). Je ne crois pas à la revendication d'efficacité, avez-vous des chiffres? +++ Cela dit, une très belle explication.
get
à une solution non résolue Future
impliquera nécessairement 2 changements de contexte de threads, ce qui, au moins quelques années auparavant, nécessiterait probablement une cinquantaine de nous .
Pour le code client, Promise permet d'observer ou de joindre un rappel lorsqu'un résultat est disponible, alors que Future attend le résultat et continue. Théoriquement tout ce qui est possible de faire avec les futurs ce qui peut être fait avec les promesses, mais en raison de la différence de style, l'API résultante pour les promesses dans différentes langues facilite le chaînage.
Aucune méthode définie dans l'interface Future, uniquement la méthode get, elle est donc en lecture seule. À propos de CompletableFuture, cet article peut être utile. completablefuture
Promise
et c'est à vous de le garder. Quand quelqu'un d'autre vous fait une promesse, vous devez attendre de voir s'il l'honore dans leFuture