Jaka jest różnica między Future
i Promise
?
Oba działają jak symbol zastępczy dla przyszłych wyników, ale gdzie jest główna różnica?
Jaka jest różnica między Future
i Promise
?
Oba działają jak symbol zastępczy dla przyszłych wyników, ale gdzie jest główna różnica?
Odpowiedzi:
Według tej dyskusji , Promise
w końcu został powołany CompletableFuture
do włączenia w Java 8, a jego javadoc wyjaśnia:
Przyszłość, która może zostać jawnie ukończona (ustawienie jej wartości i statusu) i może być wykorzystana jako CompletionStage, obsługująca zależne funkcje i akcje uruchamiane po jej zakończeniu.
Przykład podano również na liście:
f.then((s -> aStringFunction(s)).thenAsync(s -> ...);
Pamiętaj, że końcowy interfejs API jest nieco inny, ale umożliwia podobne wykonywanie asynchroniczne:
CompletableFuture<String> f = ...;
f.thenApply(this::modifyString).thenAccept(System.out::println);
(Jak dotąd nie jestem do końca zadowolony z odpowiedzi, więc oto moja próba ...)
Myślę, że komentarz Kevina Wrighta ( „Możesz złożyć obietnicę, a dotrzymanie jej zależy od ciebie. Gdy ktoś inny złoży ci obietnicę, musisz poczekać, czy ją dotrzyma w przyszłości” ) dość dobrze ją podsumowuje, ale niektóre wyjaśnienie może być przydatne.
Futures i obietnice są dość podobnymi koncepcjami, różnica polega na tym, że przyszłość jest pojemnikiem tylko do odczytu dla wyniku, który jeszcze nie istnieje, podczas gdy obietnica może być napisana (zwykle tylko raz). Java 8 CompletableFuture i Guava SettableFuture można traktować jako obietnice, ponieważ ich wartość można ustawić („ukończono”), ale implementują one również interfejs Future, dlatego nie ma różnicy dla klienta.
Wynik przyszłości zostanie ustalony przez „kogoś innego” - na podstawie obliczeń asynchronicznych. Zwróć uwagę, że FutureTask - klasyczna przyszłość - musi zostać zainicjalizowany za pomocą Callable lub Runnable, nie ma konstruktora bez argumentów, a Future i FutureTask są tylko do odczytu z zewnątrz (ustawione metody FutureTask są chronione). Wartość zostanie ustawiona na wynik obliczeń od wewnątrz.
Z drugiej strony, wynik obietnicy może być ustawiony przez „ciebie” (a właściwie przez kogokolwiek) w dowolnym momencie, ponieważ ma on metodę publicznego ustawiania. Zarówno CompletableFuture, jak i SettableFuture można tworzyć bez żadnego zadania, a ich wartość można ustawić w dowolnym momencie. Wysyłasz obietnicę do kodu klienta i wypełniasz ją później, jak chcesz.
Należy pamiętać, że CompletableFuture nie jest „czystą” obietnicą, można ją zainicjować za pomocą zadania takiego jak FutureTask, a jej najbardziej użyteczną funkcją jest niepowiązane tworzenie łańcuchów etapów przetwarzania.
Zauważ też, że obietnica nie musi być podtypem przyszłości i nie musi być tym samym przedmiotem. W Scali przyszły obiekt jest tworzony przez obliczenia asynchroniczne lub inny obiekt Promise. W C ++ sytuacja jest podobna: obiekt przyrzeczenia jest używany przez producenta, a obiekt przyszły przez konsumenta. Zaletą tego rozdzielenia jest to, że klient nie może ustalić wartości przyszłości.
Zarówno Spring, jak i EJB 3.1 mają klasę AsyncResult, która jest podobna do obietnic Scala / C ++. AsyncResult implementuje Future, ale to nie jest prawdziwa przyszłość: metody asynchroniczne w Spring / EJB zwracają inny obiekt Future tylko do odczytu poprzez magię tła, a ta druga „prawdziwa” przyszłość może zostać wykorzystana przez klienta w celu uzyskania dostępu do wyniku.
Wiem, że odpowiedź jest już zaakceptowana, ale mimo to chciałbym dodać dwa centy:
TLDR: Przyszłość i obietnica to dwie strony działania asynchronicznego: konsument / rozmówca vs. producent / realizator .
Jako wywołujący metodę asynchronicznego interfejsu API otrzymasz Future
uchwyt do wyniku obliczeń. Możesz np. Zadzwonić get()
, aby poczekać na zakończenie obliczeń i pobrać wynik.
Pomyśl teraz o tym, jak ta metoda API jest faktycznie zaimplementowana: implementator musi Future
natychmiast zwrócić . Są odpowiedzialni za ukończenie tej przyszłości, jak tylko obliczenia zostaną wykonane (co będą wiedzieć, ponieważ implementuje logikę wysyłki ;-)). Użyją Promise
/, CompletableFuture
aby to zrobić: Skonstruuj i zwróć CompletableFuture
natychmiast, i zadzwoń complete(T result)
po zakończeniu obliczeń.
Podam przykład tego, czym jest Obietnica i jak można ustawić jej wartość w dowolnym momencie, w przeciwieństwie do Przyszłości, której wartość można odczytać.
Załóżmy, że masz mamę i prosisz ją o pieniądze.
// 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);
Wynikiem tego jest:
Thank you mom for $10
Obietnica mamy została stworzona, ale czekała na jakieś „zakończenie”.
CompletableFuture<Integer> promise...
Stworzyłeś takie wydarzenie, akceptując jej obietnicę i ogłaszając plany podziękowania mamie:
promise.thenAccept...
W tym momencie mama zaczęła otwierać torebkę ... ale bardzo powoli ...
a ojciec interweniował znacznie szybciej i wypełnił obietnicę zamiast twojej mamy:
promise.complete(10);
Czy zauważyłeś wykonawcę, który napisałem wyraźnie?
Co ciekawe, jeśli zamiast tego użyjesz domyślnego domyślnego modułu wykonującego (commonPool), a ojca nie ma w domu, ale tylko mama z jej „powolną torebką”, wówczas jej obietnica się spełni, jeśli program żyje dłużej niż mama potrzebuje pieniędzy z portmonetka.
Domyślny moduł wykonujący działa jak „demon” i nie czeka na spełnienie wszystkich obietnic. Nie znalazłem dobrego opisu tego faktu ...
Nie jestem pewien, czy może to być odpowiedź, ale jak widzę, co inni powiedzieli dla kogoś, może to wyglądać tak, jakbyś potrzebował dwóch oddzielnych abstrakcji dla obu tych pojęć, tak aby jedna z nich ( Future
) była tylko widokiem do odczytu drugiej ( Promise
) ... ale tak naprawdę nie jest to potrzebne.
Na przykład spójrz, jak obietnice są zdefiniowane w javascript:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
Nacisk kładziony jest na kompozycję przy użyciu then
metody takiej jak:
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);
})
co sprawia, że obliczenia asynchroniczne wyglądają jak synchroniczne:
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);
}
co jest całkiem fajne. (Nie tak fajne, jak async-czekaj, ale async-czekaj po prostu usuwa płytkę kotła .... wtedy (funkcja (wynik) {.... z niej).
W rzeczywistości ich abstrakcja jest całkiem dobra jako konstruktor obietnic
new Promise( function(resolve, reject) { /* do it */ } );
pozwala podać dwa oddzwaniania, których można użyć do Promise
pomyślnego ukończenia lub z błędem. Tak więc tylko kod, który konstruuje, Promise
może go ukończyć, a kod, który odbiera już skonstruowany Promise
obiekt, ma widok tylko do odczytu.
Dzięki dziedziczeniu powyższe można osiągnąć, jeśli rozwiązania i odrzucenia są chronionymi metodami.
CompletableFuture
może mieć pewne podobieństwo do Promise
ale ,Promise
ale nadal tak nie jest , ponieważ sposób, w jaki ma być spożywany, jest inny: Promise
wynik jest zużywany przez wywołanie then(function)
, a funkcja jest wykonywana w kontekście producenta natychmiast po wywołaniu przez producenta resolve
. Future
Wynik jest zużywana jest przez wywołanie get
co powoduje nić konsumentów czekać aż gwint producent wygenerował wartość, a następnie przetwarza je na konsumenta. Future
jest z natury wielowątkowy, ale ...
Promise
tylko jednego wątku (w rzeczywistości jest to dokładne środowisko, dla którego zostały pierwotnie zaprojektowane: aplikacje javascript mają zazwyczaj tylko jeden wątek, więc nie możnaFuture
tam zaimplementować ). Promise
jest zatem znacznie lżejszy i wydajniejszy niż Future
, ale Future
może być pomocny w sytuacjach, które są bardziej złożone i wymagają współpracy między wątkami, których nie można łatwo za pomocą Promise
s. Podsumowując: Promise
jest modelem push, podczas gdy Future
jest modelem pull (por. Iterable vs Observable)
XMLHttpRequest
). Nie wierzę w deklarację efektywności, czy zdarza ci się mieć jakieś liczby? +++ To powiedziawszy, bardzo miłe wytłumaczenie.
get
do nierozwiązania Future
będzie wymagało 2 przełączników kontekstowych wątków, co przynajmniej kilka lat temu prawdopodobnie wymagałoby około 50 nas .
W przypadku kodu klienta Promise służy do obserwowania lub dołączania oddzwaniania, gdy wynik jest dostępny, natomiast Future ma czekać na wynik, a następnie kontynuować. Teoretycznie wszystko, co można zrobić z przyszłością, co można zrobić z obietnicami, ale ze względu na różnicę stylu wynikowe API dla obietnic w różnych językach ułatwia tworzenie łańcuchów.
Brak metody ustawionej w interfejsie Future, tylko metoda get, więc jest tylko do odczytu. O CompletableFuture ten artykuł może być pomocny. pełna przyszłość
Promise
i to zależy od ciebie, aby go zatrzymać. Gdy ktoś inny złoży ci obietnicę, musisz poczekać, aby zobaczyć, czy ją dotrzymaFuture