Qual é a diferença entre um futuro e uma promessa?


275

Qual é a diferença entre Futuree Promise?
Ambos agem como um espaço reservado para resultados futuros, mas qual é a principal diferença?


100
Você pode fazer um Promisee depende de você mantê-lo. Quando alguém lhe faz uma promessa que você deve esperar para ver se eles honrá-lo noFuture
Kevin Wright


30
Um dos artigos da Wikipédia menos votos que eu já li
Fulluphigh

Respostas:


146

De acordo com essa discussão , Promisefinalmente foi chamado CompletableFuturepara inclusão no Java 8 e seu javadoc explica:

Um futuro que pode ser explicitamente concluído (definindo seu valor e status) e pode ser usado como um CompletionStage, suportando funções e ações dependentes que são acionadas após sua conclusão.

Um exemplo também é dado na lista:

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

Observe que a API final é um pouco diferente, mas permite execução assíncrona semelhante:

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

78
Não é culpa sua, Assylias, mas esse extrato de javadoc precisa de uma solução séria de um autor técnico decente. Na minha quinta leitura, posso começar a apreciar o que está tentando dizer ... e chego a isso com uma compreensão dos futuros e promessas já em vigor!
Beterraba-Beterraba

2
@ Beterraba-beterraba parece que já aconteceu até agora.
herman

1
@ Herman Obrigado - eu atualizei o link para apontar para a versão final do javadoc.
Assilias 15/06/2015

7
@ Beterraba-beterraba Você deve conferir o documento para o método Excepcionalmente. Seria um poema maravilhoso, mas é uma falha excepcional na documentação legível.
Fulluphigh

4
Para quem se perguntar, @Fulluphigh está se referindo a isso . Parece que foi removido / reformulado em Java 8.
Cedric Reichenbach

148

(Eu não estou completamente feliz com as respostas até agora, então aqui está minha tentativa ...)

Acho que o comentário de Kevin Wright ( "Você pode fazer uma promessa e depende de você cumpri-la. Quando alguém lhe faz uma promessa, você deve esperar para ver se a honrará no futuro" ) resume muito bem, mas alguns explicação pode ser útil.

Futuros e promessas são conceitos bastante semelhantes, a diferença é que o futuro é um contêiner somente leitura para um resultado que ainda não existe, enquanto uma promessa pode ser escrita (normalmente apenas uma vez). O Java 8 CompletableFuture e o Guava SettableFuture podem ser considerados promissores, porque seu valor pode ser definido ("concluído"), mas eles também implementam a interface Future, portanto, não há diferença para o cliente.

O resultado do futuro será definido por "outra pessoa" - pelo resultado de uma computação assíncrona. Observe como o FutureTask - um futuro clássico - deve ser inicializado com um Callable ou Runnable, não há construtor sem argumentos e o Future e o FutureTask são somente leitura de fora (os métodos definidos do FutureTask estão protegidos). O valor será definido como o resultado da computação a partir do interior.

Por outro lado, o resultado de uma promessa pode ser definido por "você" (ou, de fato, por qualquer pessoa) a qualquer momento, porque possui um método público de configuração. CompletableFuture e SettableFuture podem ser criados sem qualquer tarefa e seu valor pode ser definido a qualquer momento. Você envia uma promessa ao código do cliente e a cumpre mais tarde, conforme desejar.

Observe que CompletableFuture não é uma promessa "pura", pode ser inicializada com uma tarefa como a FutureTask, e seu recurso mais útil é o encadeamento não relacionado das etapas de processamento.

Observe também que uma promessa não precisa ser um subtipo de futuro e não precisa ser o mesmo objeto. No Scala, um objeto Futuro é criado por uma computação assíncrona ou por um objeto Promise diferente . Em C ++, a situação é semelhante: o objeto de promessa é usado pelo produtor e o objeto futuro pelo consumidor. A vantagem dessa separação é que o cliente não pode definir o valor do futuro.

O Spring e o EJB 3.1 têm uma classe AsyncResult, que é semelhante às promessas do Scala / C ++. O AsyncResult implementa Future, mas este não é o futuro real: métodos assíncronos no Spring / EJB retornam um objeto Future diferente, somente leitura, através de alguma mágica de fundo, e esse segundo futuro "real" pode ser usado pelo cliente para acessar o resultado.


116

Estou ciente de que já existe uma resposta aceita, mas gostaria de acrescentar meus dois centavos:

TLDR: Futuro e Promessa são os dois lados de uma operação assíncrona: consumidor / chamador vs. produtor / implementador .

Como chamador de um método de API assíncrono, você receberá Futureum identificador para o resultado da computação. Você pode, por exemplo, solicitar get()que o cálculo seja concluído e recuperar o resultado.

Agora pense em como esse método de API é realmente implementado: O implementador deve retornar um Futureimediatamente. Eles são responsáveis ​​por concluir esse futuro assim que o cálculo for concluído (o que eles saberão porque está implementando a lógica de despacho ;-)). Eles usarão um Promise/ CompletableFuturepara fazer exatamente isso: Construa e retorne o CompletableFutureimediatamente, e chame complete(T result)assim que o cálculo estiver concluído.


1
Isso implica que uma promessa é sempre uma subclasse do futuro e que a capacidade de gravação do futuro é apenas obscurecida pelo tipo?
devios1

Eu não acho que isso esteja implícito . Em termos de implementação, geralmente será o caso (por exemplo, em Java, Scala).
Rahel Lüthy

74

Vou dar um exemplo do que é Promise e como seu valor pode ser definido a qualquer momento, ao contrário de Future, cujo valor é apenas legível.

Suponha que você tenha uma mãe e peça dinheiro a ela.

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

Saída disso é:

Thank you mom for $10

A promessa da mamãe foi criada, mas esperou por algum evento de "conclusão".

CompletableFuture<Integer> promise...

Você criou esse evento, aceitando a promessa dela e anunciando seus planos de agradecer à sua mãe:

promise.thenAccept...

Nesse momento a mãe começou a abrir a bolsa ... mas muito devagar ...

e pai interferiram muito mais rápido e cumpriram a promessa em vez de sua mãe:

promise.complete(10);

Você notou um executor que eu escrevi explicitamente?

Curiosamente, se você usar um executor implícito padrão (commonPool) e o pai não estiver em casa, mas apenas a mãe com sua "bolsa lenta", sua promessa só será concluída se o programa viver mais do que a mãe precisa obter dinheiro com o Bolsa.

O executor padrão age como um "daemon" e não espera que todas as promessas sejam cumpridas. Não encontrei uma boa descrição desse fato ...


8
É muito divertido ler este! Eu não acho que poderia esquecer o futuro e prometer mais.
user1532146

2
Isso deve ser aceito como resposta. É como ler uma história. Graças @Vladimir
Phillen

Obrigado @Vladimir
intvprep 03/03

9

Não tenho certeza se isso pode ser uma resposta, mas como vejo o que os outros disseram para alguém, pode parecer que você precise de duas abstrações separadas para esses dois conceitos, de modo que um deles ( Future) seja apenas uma visualização somente leitura do outro ( Promise) ... mas, na verdade, isso não é necessário.

Por exemplo, veja como as promessas são definidas em javascript:

https://promisesaplus.com/

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

O foco está na composibilidade usando o 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);
})

que faz com que a computação assíncrona pareça síncrona:

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

o que é bem legal. (Não é tão legal quanto o assíncrono-aguardar, mas o assíncrono-aguarda apenas remove o clichê ... então (function (result) {.... dele).

E, na verdade, sua abstração é muito boa como construtora de promessas

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

permite fornecer dois retornos de chamada que podem ser usados ​​para concluir com Promiseêxito ou com um erro. Para que apenas o código que constrói o Promisepossa concluí-lo e o código que recebe um Promiseobjeto já construído tenha a exibição somente leitura.

Com a herança, o acima pode ser alcançado se resolver e rejeitar forem métodos protegidos.


4
+1. Esta é a resposta correta para esta pergunta. CompletableFuturepode ter alguma semelhança com a, Promisemas ainda não éPromise , porque a maneira como se destina a ser consumida é diferente: Promiseo resultado de a é consumido pela chamada then(function)e a função é executada no contexto do produtor imediatamente após a ligação do produtor. resolve. O Futureresultado de A é consumido pela chamada, getque faz com que o encadeamento do consumidor aguarde até que o encadeamento do produtor gere o valor e o processe no consumidor. Futureé inerentemente multithreaded, mas ...
Periata Breatta

5
... é inteiramente possível usar um Promisecom apenas um thread (e, de fato, esse é o ambiente preciso para o qual foram projetados originalmente: aplicativos javascript geralmente têm apenas um thread, portanto você não pode implementá Future-lo). Promiseé, portanto, muito mais leve e eficiente do que Future, mas Futurepode ser útil em situações mais complexas e que exijam cooperação entre encadeamentos que não podem ser facilmente organizados usando Promises. Para resumir: Promiseé um modelo de impulso, enquanto que Futureé um modelo de recepção (cf Iterable vs observável)
Periata Breatta

@PeriataBreatta Mesmo em um ambiente de thread único, deve haver algo cumprindo a promessa (que normalmente é executado como um thread diferente, por exemplo, um XMLHttpRequest). Eu não acredito na alegação de eficiência, você tem alguns números? +++ Dito isto, uma explicação muito boa.
Maaartinus 7/11

1
@maaartinus - sim, algo deve cumprir a promessa, mas pode (e de fato é em muitos casos) ser feito usando um loop de nível superior que pesquisa mudanças no estado externo e resolve as promessas relacionadas às ações que foram finalizadas. Em termos de eficiência, não tenho números concretos para o Promises especificamente, mas observe que a solicitação getde uma solução Futurenão resolvida envolverá necessariamente duas alternâncias de contexto de encadeamento, que pelo menos alguns anos atrás provavelmente exigiriam cerca de 50 nós .
Periata Breatta 11/11

@PeriataBreatta Na verdade, seu comentário deve ser a solução aceita. Eu estava procurando uma explicação (pull / push, single / multi-thread) como a sua.
Thomas Jacob

5

Para o código do cliente, o Promise é para observar ou anexar retorno de chamada quando um resultado estiver disponível, enquanto Futuro é aguardar pelo resultado e continuar. Teoricamente, tudo o que é possível fazer com os futuros pode ser feito com promessas, mas devido à diferença de estilo, a API resultante de promessas em diferentes idiomas facilita o encadeamento.


2

Nenhum método definido na interface Futuro, apenas o método get, portanto, é somente leitura. Sobre o CompletableFuture, este artigo pode ser útil. completablefuture

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.