Was ist der Unterschied zwischen Future
und Promise
?
Beide fungieren als Platzhalter für zukünftige Ergebnisse, aber wo liegt der Hauptunterschied?
Was ist der Unterschied zwischen Future
und Promise
?
Beide fungieren als Platzhalter für zukünftige Ergebnisse, aber wo liegt der Hauptunterschied?
Antworten:
Nach dieser Diskussion , Promise
wird schließlich wurde genannt CompletableFuture
für die Aufnahme in Java 8 und seine javadoc erklärt:
Eine Zukunft, die explizit abgeschlossen werden kann (Festlegen ihres Werts und Status) und als CompletionStage verwendet werden kann und abhängige Funktionen und Aktionen unterstützt, die nach ihrem Abschluss ausgelöst werden.
Ein Beispiel ist auch in der Liste angegeben:
f.then((s -> aStringFunction(s)).thenAsync(s -> ...);
Beachten Sie, dass sich die endgültige API geringfügig unterscheidet, jedoch eine ähnliche asynchrone Ausführung ermöglicht:
CompletableFuture<String> f = ...;
f.thenApply(this::modifyString).thenAccept(System.out::println);
(Ich bin mit den Antworten bisher nicht ganz zufrieden, also hier ist mein Versuch ...)
Ich denke, dass Kevin Wrights Kommentar ( "Sie können ein Versprechen machen und es liegt an Ihnen, es zu halten. Wenn jemand anderes Ihnen ein Versprechen macht, müssen Sie abwarten, ob er es in Zukunft einhält" ) fasst es ziemlich gut zusammen, aber einige Erklärung kann nützlich sein.
Futures und Versprechen sind ziemlich ähnliche Konzepte. Der Unterschied besteht darin, dass eine Zukunft ein schreibgeschützter Container für ein Ergebnis ist, das noch nicht existiert, während ein Versprechen geschrieben werden kann (normalerweise nur einmal). Die Java 8 CompletableFuture und die Guava SettableFuture können als Versprechen angesehen werden, da ihr Wert festgelegt ("abgeschlossen") werden kann, sie aber auch die Future-Schnittstelle implementieren, sodass für den Client kein Unterschied besteht.
Das Ergebnis der Zukunft wird von "jemand anderem" festgelegt - durch das Ergebnis einer asynchronen Berechnung. Beachten Sie, dass FutureTask - eine klassische Zukunft - mit einem Callable oder Runnable initialisiert werden muss , es keinen Konstruktor ohne Argumente gibt und sowohl Future als auch FutureTask von außen schreibgeschützt sind (die festgelegten Methoden von FutureTask sind geschützt). Der Wert wird von innen auf das Ergebnis der Berechnung gesetzt.
Andererseits kann das Ergebnis eines Versprechens jederzeit von "Ihnen" (oder tatsächlich von irgendjemandem) festgelegt werden, da es über eine öffentliche Setter-Methode verfügt. Sowohl CompletableFuture als auch SettableFuture können ohne Aufgabe erstellt und ihr Wert kann jederzeit festgelegt werden. Sie senden ein Versprechen an den Kundencode und erfüllen es später nach Ihren Wünschen.
Beachten Sie, dass CompletableFuture kein "reines" Versprechen ist, sondern mit einer Aufgabe wie FutureTask initialisiert werden kann. Die nützlichste Funktion ist die unabhängige Verkettung von Verarbeitungsschritten.
Beachten Sie auch, dass ein Versprechen kein Subtyp der Zukunft sein muss und nicht dasselbe Objekt sein muss. In Scala wird ein Future-Objekt durch eine asynchrone Berechnung oder durch ein anderes Promise-Objekt erstellt. In C ++ ist die Situation ähnlich: Das Versprechen-Objekt wird vom Produzenten und das zukünftige Objekt vom Verbraucher verwendet. Der Vorteil dieser Trennung ist, dass der Kunde den Wert der Zukunft nicht festlegen kann.
Sowohl Spring als auch EJB 3.1 haben eine AsyncResult-Klasse, die den Scala / C ++ - Versprechungen ähnelt. AsyncResult implementiert Future, aber dies ist nicht die wirkliche Zukunft: Asynchrone Methoden in Spring / EJB geben durch Hintergrundmagie ein anderes schreibgeschütztes Future-Objekt zurück, und diese zweite "echte" Zukunft kann vom Client verwendet werden, um auf das Ergebnis zuzugreifen.
Mir ist bewusst, dass es bereits eine akzeptierte Antwort gibt, möchte aber trotzdem meine zwei Cent hinzufügen:
TLDR: Future und Promise sind die beiden Seiten einer asynchronen Operation: Consumer / Caller vs. Producer / Implementor .
Als Aufrufer einer asynchronen API-Methode erhalten Sie Future
ein Handle für das Ergebnis der Berechnung. Sie können es beispielsweise aufrufen, get()
um zu warten, bis die Berechnung abgeschlossen ist, und das Ergebnis abzurufen.
Stellen Sie sich nun vor, wie diese API-Methode tatsächlich implementiert ist: Der Implementierer muss Future
sofort eine zurückgeben. Sie sind dafür verantwortlich, diese Zukunft abzuschließen, sobald die Berechnung abgeschlossen ist (was sie wissen werden, weil es die Versandlogik implementiert ;-)). Sie werden ein Promise
/ verwenden CompletableFuture
, um genau das zu tun: Konstruieren und geben Sie das CompletableFuture
sofort zurück und rufen Sie auf, complete(T result)
sobald die Berechnung abgeschlossen ist.
Ich werde ein Beispiel geben, was Versprechen ist und wie sein Wert jederzeit eingestellt werden kann, im Gegensatz zu Future, wobei dieser Wert nur lesbar ist.
Angenommen, Sie haben eine Mutter und bitten sie um Geld.
// 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);
Die Ausgabe davon ist:
Thank you mom for $10
Mamas Versprechen wurde erstellt, wartete aber auf ein "Abschluss" -Ereignis.
CompletableFuture<Integer> promise...
Sie haben ein solches Ereignis ins Leben gerufen, ihr Versprechen angenommen und Ihre Pläne angekündigt, Ihrer Mutter zu danken:
promise.thenAccept...
In diesem Moment fing Mama an, ihre Handtasche zu öffnen ... aber sehr langsam ...
und Vater mischte sich viel schneller ein und erfüllte das Versprechen anstelle deiner Mutter:
promise.complete(10);
Haben Sie einen Testamentsvollstrecker bemerkt, den ich explizit geschrieben habe?
Interessanterweise wird ihr Versprechen nur dann erfüllt, wenn Sie stattdessen einen impliziten Standard-Executor (commonPool) verwenden und der Vater nicht zu Hause ist, sondern nur die Mutter mit ihrem "langsamen Geldbeutel", wenn das Programm länger lebt, als die Mutter Geld von der benötigt Geldbörse.
Der Standard-Executor verhält sich wie ein "Daemon" und wartet nicht darauf, dass alle Versprechen erfüllt werden. Ich habe keine gute Beschreibung dieser Tatsache gefunden ...
Ich bin mir nicht sicher, ob dies eine Antwort sein kann, aber wie ich sehe, was andere für jemanden gesagt haben, sieht es möglicherweise so aus, als ob Sie für beide Konzepte zwei separate Abstraktionen benötigen, sodass eine von ihnen ( Future
) nur eine schreibgeschützte Ansicht der anderen ist ( Promise
) ... aber eigentlich wird das nicht benötigt.
Schauen Sie sich zum Beispiel an, wie Versprechen in Javascript definiert sind:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
Der Fokus liegt auf der Zusammensetzbarkeit mit der then
Methode wie:
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);
})
Dadurch sieht die asynchrone Berechnung synchron aus:
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);
}
Das ist ziemlich cool. (Nicht so cool wie async-await, aber async-await entfernt nur die Boilerplate .... then (function (result) {.... from it).
Und tatsächlich ist ihre Abstraktion als Versprechenskonstruktor ziemlich gut
new Promise( function(resolve, reject) { /* do it */ } );
Mit dieser Option können Sie zwei Rückrufe bereitstellen, mit denen Sie den Vorgang entweder Promise
erfolgreich oder mit einem Fehler abschließen können . Damit nur der Code, der das Promise
erstellt, es vervollständigen kann und der Code, der ein bereits erstelltes Promise
Objekt empfängt, die schreibgeschützte Ansicht hat.
Mit der Vererbung kann das Obige erreicht werden, wenn Auflösung und Zurückweisung geschützte Methoden sind.
CompletableFuture
Möglicherweise hat es eine gewisse Ähnlichkeit mit a, Promise
aber es ist immer noch keinePromise
, da die Art und Weise, wie es konsumiert werden soll, unterschiedlich ist: Promise
Das Ergebnis von a wird durch Aufrufen konsumiert then(function)
, und die Funktion wird unmittelbar nach dem Aufruf des Produzenten im Kontext des Produzenten ausgeführt resolve
. Future
Das Ergebnis von A wird durch einen Aufruf verbraucht, get
wodurch der Consumer-Thread wartet, bis der Producer-Thread den Wert generiert hat, und ihn dann im Consumer verarbeitet. Future
ist von Natur aus multithreaded, aber ...
Promise
mit nur einem Thread zu verwenden (und tatsächlich ist dies die genaue Umgebung, für die sie ursprünglich entwickelt wurden: Javascript-Anwendungen haben im Allgemeinen nur einen einzigen Thread, sodass Sie sie dort nicht implementieren Future
können). Promise
ist daher viel leichter und effizienter als Future
, Future
kann aber in Situationen hilfreich sein, die komplexer sind und die Zusammenarbeit zwischen Threads erfordern, die mit Promise
s nicht einfach angeordnet werden können . Zusammenfassend: Promise
ist ein Push-Modell, während Future
es ein Pull-Modell ist (vgl. Iterable vs Observable)
XMLHttpRequest
. B. ein ). Ich glaube dem Effizienzanspruch nicht, haben Sie zufällig Zahlen? +++ Das heißt, eine sehr schöne Erklärung.
get
eines ungelösten Future
Problems zwangsläufig zwei Thread-Kontextwechsel erfordert, für die vor mindestens einigen Jahren wahrscheinlich etwa 50 US erforderlich waren .
Bei Client-Code dient Promise zum Beobachten oder Anhängen eines Rückrufs, wenn ein Ergebnis verfügbar ist, während Future auf das Ergebnis warten und dann fortfahren soll. Theoretisch alles, was mit Futures zu tun ist, was mit Versprechungen getan werden kann, aber aufgrund des Stilunterschieds erleichtert die resultierende API für Versprechungen in verschiedenen Sprachen die Verkettung.
Keine festgelegte Methode in der Future-Schnittstelle, nur get-Methode, daher schreibgeschützt. Über CompletableFuture kann dieser Artikel hilfreich sein. vervollständigbare Zukunft
Promise
und es liegt an Ihnen, es zu behalten. Wenn jemand anderes Ihnen ein Versprechen macht, müssen Sie abwarten, ob er es imFuture