Qual è la differenza tra un futuro e una promessa?


275

Qual è la differenza tra Futuree Promise?
Entrambi agiscono come segnaposto per risultati futuri, ma dov'è la differenza principale?


100
Puoi crearne uno Promisee spetta a te mantenerlo. Quando qualcun altro ti fa una promessa devi aspettare per vedere se lo onorano nelFuture
Kevin Wright

1
wikipedia Futures e promesse
wener

30
Uno degli articoli di Wikipedia meno utili che abbia mai letto
Fulluphigh,

Risposte:


146

Secondo questa discussione , Promiseè stato finalmente richiesto l' CompletableFutureinclusione in Java 8 e il suo javadoc spiega:

Un futuro che può essere esplicitamente completato (impostandone il valore e lo stato) e che può essere utilizzato come Fase di completamento, supportando funzioni e azioni dipendenti che si innescano al suo completamento.

Un esempio è riportato anche nell'elenco:

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

Si noti che l'API finale è leggermente diversa ma consente un'esecuzione asincrona simile:

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

78
Assylias non è colpa tua, ma quell'estratto di javadoc ha bisogno di un serio rinnovamento da parte di un autore di tecnologia decente. Al mio quinto read-through posso solo iniziare ad apprezzare ciò che sta cercando di dire ... e arrivo a questo con una comprensione dei futuri e delle promesse già in atto!
Beetroot-Beetroot

2
@ Barbabietola-Barbabietola sembra che sia successo ormai.
herman,

1
@herman Grazie - Ho aggiornato il link per puntare alla versione finale di javadoc.
assylias,

7
@ Barbabietola rossa Dovresti controllare il documento per il metodo Eccezionalmente. Sarebbe una poesia meravigliosa, ma è un eccezionale fallimento della documentazione leggibile.
Fulluphigh,

4
Per chiunque si chieda, @Fulluphigh si riferisce a questo . Sembra che sia stato rimosso / revisionato in Java 8.
Cedric Reichenbach il

148

(Non sono completamente soddisfatto delle risposte finora, quindi ecco il mio tentativo ...)

Penso che il commento di Kevin Wright ( "Puoi fare una promessa e spetta a te mantenerla. Quando qualcun altro ti fa una promessa devi aspettare per vedere se lo onorano in futuro" ) lo riassume abbastanza bene, ma alcuni la spiegazione può essere utile.

Futuri e promesse sono concetti abbastanza simili, la differenza è che un futuro è un contenitore di sola lettura per un risultato che non esiste ancora, mentre una promessa può essere scritta (normalmente solo una volta). Java 8 CompletableFuture e Guava SettableFuture possono essere considerati promesse, perché il loro valore può essere impostato ("completato"), ma implementano anche l'interfaccia Future, quindi non c'è differenza per il client.

Il risultato del futuro sarà impostato da "qualcun altro" - dal risultato di un calcolo asincrono. Nota come FutureTask - un futuro classico - deve essere inizializzato con Callable o Runnable, non esiste un costruttore senza argomenti e sia Future che FutureTask sono di sola lettura dall'esterno (i metodi impostati di FutureTask sono protetti). Il valore verrà impostato sul risultato del calcolo dall'interno.

D'altra parte, il risultato di una promessa può essere impostato da "voi" (o di fatto da chiunque) in qualsiasi momento perché ha un metodo di impostazione pubblica. Sia CompletableFuture che SettableFuture possono essere creati senza alcuna attività e il loro valore può essere impostato in qualsiasi momento. Invia una promessa al codice cliente e adempia in seguito come desideri.

Si noti che CompletableFuture non è una promessa "pura", può essere inizializzata con un'attività proprio come FutureTask e la sua caratteristica più utile è il concatenamento non correlato delle fasi di elaborazione.

Si noti inoltre che una promessa non deve essere un sottotipo di futuro e non deve essere lo stesso oggetto. In Scala un oggetto Future viene creato da un calcolo asincrono o da un diverso oggetto Promise. In C ++ la situazione è simile: l'oggetto promessa viene utilizzato dal produttore e l'oggetto futuro dal consumatore. Il vantaggio di questa separazione è che il cliente non può impostare il valore del futuro.

Sia Spring che EJB 3.1 hanno una classe AsyncResult, che è simile alle promesse di Scala / C ++. AsyncResult implementa Future ma questo non è il vero futuro: i metodi asincroni in Spring / EJB restituiscono un oggetto Future diverso e di sola lettura attraverso un po 'di magia di fondo, e questo secondo "reale" futuro può essere utilizzato dal client per accedere al risultato.


116

Sono consapevole che esiste già una risposta accettata, ma vorrei aggiungere i miei due centesimi:

TLDR: Future e Promise sono i due lati di un'operazione asincrona: consumatore / chiamante vs. produttore / implementatore .

Come chiamante di un metodo API asincrono, otterrai Futureun handle per il risultato del calcolo. Ad esempio, è possibile chiamarlo get()per attendere il completamento del calcolo e il recupero del risultato.

Ora pensa a come viene effettivamente implementato questo metodo API: l' implementatore deve restituire Futureimmediatamente un . Sono responsabili del completamento di quel futuro non appena il calcolo sarà completato (cosa che sapranno perché sta implementando la logica di invio ;-)). Useranno un Promise/ CompletableFutureper fare proprio questo: costruisci e restituisci CompletableFutureimmediatamente e chiama complete(T result)una volta fatto il calcolo.


1
Ciò implica che una Promessa è sempre una sottoclasse del Futuro e che la scrivibilità del Futuro è solo oscurata dal tipo?
devios1

Non penso che sia implicito . Per quanto riguarda l'implementazione, spesso lo sarà (ad esempio in Java, Scala).
Rahel Lüthy,

74

Darò un esempio di ciò che è Promessa e di come il suo valore potrebbe essere impostato in qualsiasi momento, al contrario di Future, il cui valore è solo leggibile.

Supponi di avere una mamma e di chiederle soldi.

// 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); 

Il risultato è:

Thank you mom for $10

La promessa della mamma è stata creata, ma ha aspettato qualche evento di "completamento".

CompletableFuture<Integer> promise...

Hai creato un evento del genere, accettando la sua promessa e annunciando i tuoi piani per ringraziare tua madre:

promise.thenAccept...

In questo momento la mamma ha iniziato ad aprire la borsa ... ma molto lentamente ...

e tuo padre ha interferito molto più velocemente e ha completato la promessa invece di tua madre:

promise.complete(10);

Hai notato un esecutore che ho scritto esplicitamente?

È interessante notare che se usi invece un esecutore implicito predefinito (commonPool) e il padre non è a casa, ma solo la mamma con la sua "borsa lenta", la sua promessa si completerà solo se il programma vive più a lungo di quanto la mamma abbia bisogno di ottenere soldi dal borsa.

L'esecutore predefinito si comporta come un "demone" e non aspetta che tutte le promesse vengano mantenute. Non ho trovato una buona descrizione di questo fatto ...


8
È così divertente leggere questo! Non credo di poter più dimenticare il futuro e promettere.
user1532146

2
Questa deve essere accettata come risposta. È proprio come leggere una storia. Grazie @Vladimir
Phillen l'

Grazie @Vladimir
intvprep

9

Non sono sicuro che questa possa essere una risposta, ma come vedo quello che gli altri hanno detto per qualcuno potrebbe sembrare che tu abbia bisogno di due astrazioni separate per entrambi questi concetti in modo che uno di essi ( Future) sia solo una vista di sola lettura dell'altro ( Promise) ... ma in realtà questo non è necessario.

Ad esempio, dai un'occhiata a come sono definite le promesse in javascript:

https://promisesaplus.com/

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

L'attenzione si concentra sulla componibilità usando il thenmetodo come:

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);
})

che rende il calcolo asincrono simile al sincrono:

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);
}

che è abbastanza bello. (Non fico come async-waitit ma async-wait rimuove solo la piastra della caldaia .... quindi (funzione (risultato) {.... da essa).

E in realtà la loro astrazione è abbastanza buona come il costruttore della promessa

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

consente di fornire due callback che possono essere utilizzati per completare Promisecorrettamente o con un errore. In modo che solo il codice che costruisce il Promisepuò completarlo e il codice che riceve un Promiseoggetto già costruito ha la vista di sola lettura.

Con l'ereditarietà si può ottenere quanto sopra se la risoluzione e il rifiuto sono metodi protetti.


4
+1. Questa è la risposta corretta a questa domanda. CompletableFuturepotrebbe avere una certa somiglianza con un Promisema non è ancora unPromise , perché il modo in cui è destinato a essere consumato è diverso: Promiseil risultato di un viene consumato chiamando then(function)e la funzione viene eseguita nel contesto del produttore immediatamente dopo che il produttore chiama resolve. Il Futurerisultato di A viene consumato chiamando il getche induce il thread consumer ad attendere fino a quando il thread del produttore ha generato il valore, quindi lo elabora nel consumer. Futureè intrinsecamente multithread, ma ...
Periata Breatta,

5
... è del tutto possibile utilizzare un Promisecon un solo thread (e in effetti è l'ambiente preciso per cui sono stati originariamente progettati: le applicazioni javascript generalmente hanno un solo thread, quindi non è possibile implementarlo Futurelì). Promiseè quindi molto più leggero ed efficiente di Future, ma Futurepuò essere utile in situazioni che sono più complesse e richiedono la cooperazione tra thread che non possono essere facilmente organizzati usando Promises. Riassumendo: Promiseè un modello push, mentre Futureè un modello pull (cfr Iterable vs Observable)
Periata Breatta,

@PeriataBreatta Anche in un ambiente a thread singolo, ci deve essere qualcosa che mantiene la promessa (che in genere funziona come un thread diverso, ad esempio, un XMLHttpRequest). Non credo all'affermazione dell'efficienza, ti capita di avere delle cifre? +++ Detto questo, una spiegazione molto bella.
Maaartinus,

1
@maaartinus - sì, qualcosa deve mantenere la promessa, ma può (e in effetti in molti casi lo è) fare usando un ciclo di livello superiore che esegue il polling per i cambiamenti nello stato esterno e risolve qualsiasi promessa si riferisca ad azioni che sono terminate. Per quanto riguarda l'efficienza, non ho cifre precise per le promesse in particolare, ma nota che chiamare getun irrisolto Futureimplicherà necessariamente 2 cambi di contesto del thread, che almeno qualche anno fa avrebbe richiesto circa 50 di noi .
Periata Breatta,

@PeriataBreatta In realtà il tuo commento dovrebbe essere la soluzione accettata. Stavo cercando una spiegazione (pull / push, single / multi-thread) come la tua.
Thomas Jacob,

5

Per il codice client, Promise è per osservare o allegare la richiamata quando è disponibile un risultato, mentre Future deve attendere il risultato e quindi continuare. Teoricamente tutto ciò che è possibile fare con i futuri è ciò che si può fare con le promesse, ma a causa della differenza di stile, l'API risultante per le promesse in diverse lingue facilita il concatenamento.


2

Nessun metodo impostato nell'interfaccia futura, ottieni solo metodo, quindi è di sola lettura. Informazioni su CompletableFuture, questo articolo potrebbe essere utile. completablefuture

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.