Quais são as diferenças entre adiado, promissor e futuro em JavaScript?

300

Quais são as diferenças entre diferidos, promessas e futuros?
Existe uma teoria geralmente aprovada por trás de todos esses três?

Torre
fonte
11
Eu não acho que isso tem alguma coisa a ver com jQuery ...
BoltClock
11
Vale a pena ler isso: msdn.microsoft.com/en-us/scriptjunkie/gg723713
jfriend00:
8
Eu não os usei, mas aqui está uma introdução muito boa na wikipedia en.wikipedia.org/wiki/Futures_and_promises . Embora eu não entenda completamente o caso de uso corretamente. Em uma linguagem orientada a eventos assíncrona como javascript. À primeira vista, não consigo ver o que eles oferecem sobre retornos de chamada, além de talvez uma API mais limpa. Eu adoraria se alguém pudesse fornecer um exemplo de caso de uso e mostrar como esses conceitos são aplicados e por que retornos de chamada seriam uma solução ineficiente. @uri, isso não tem nada a ver com jQuery. A tag jQuery pode ser removida, por favor
AshHeskes
2
@ jfriend00 ótimo link, provavelmente deve ser trabalhado em uma resposta.
fncomp 26/07/11

Respostas:

97

À luz da aparente aversão a como tentei responder à pergunta do OP. A resposta literal é que uma promessa é algo compartilhado com outros objetos, enquanto um adiado deve ser mantido em sigilo. Principalmente, um diferido (que geralmente estende a Promessa) pode se resolver, enquanto uma promessa pode não ser capaz de fazê-lo.

Se você estiver interessado nas minúcias, examine Promessas / A + .


Até onde eu sei, o objetivo geral é melhorar a clareza e afrouxar o acoplamento por meio de uma interface padronizada. Veja a leitura sugerida de @ jfriend00:

Em vez de transmitir diretamente retornos de chamada para funções, algo que pode levar a interfaces fortemente acopladas, o uso de promessas permite separar as preocupações com o código que é síncrono ou assíncrono.

Pessoalmente, achei o adiamento especialmente útil ao lidar com, por exemplo, modelos preenchidos por solicitações assíncronas, carregar scripts que possuem redes de dependências e fornecer feedback do usuário para formar dados de maneira não-bloqueadora.

Na verdade, compare a forma pura de retorno de chamada de algo após carregar o CodeMirror no modo JS de forma assíncrona (desculpas, eu não uso o jQuery há algum tempo ):

/* assume getScript has signature like: function (path, callback, context) 
   and listens to onload && onreadystatechange */
$(function () {
   getScript('path/to/CodeMirror', getJSMode);

   // onreadystate is not reliable for callback args.
   function getJSMode() {
       getScript('path/to/CodeMirror/mode/javascript/javascript.js', 
           ourAwesomeScript);
   };

   function ourAwesomeScript() {
       console.log("CodeMirror is awesome, but I'm too impatient.");
   };
});

Para a versão formulada das promessas (novamente, desculpas, não estou atualizado no jQuery):

/* Assume getScript returns a promise object */
$(function () {
   $.when(
       getScript('path/to/CodeMirror'),
       getScript('path/to/CodeMirror/mode/javascript/javascript.js')
   ).then(function () {
       console.log("CodeMirror is awesome, but I'm too impatient.");
   });
});

Desculpas pelo semi-pseudo-código, mas espero que deixe a idéia central um pouco clara. Basicamente, retornando uma promessa padronizada, você pode passá-la, permitindo um agrupamento mais claro.

fncomp
fonte
10
Embora essa resposta possa ser útil, ela não aborda de fato a questão: os chamados diferidos são futuros ou promessas, dependendo da implementação.
awdz9nld
@ MartinKällman Você está certo! Eu não tinha revisitado isso há um tempo e aprendi um pouco. Postarei uma resposta separada abaixo, mas deixo isso, pois as pessoas parecem ter se beneficiado com o exemplo de uso.
Fncomp
@ MartinKällman, considerou escrever uma nova resposta. No entanto, acho que o OP realmente queria saber para que servem as promessas e os diferidos. A resposta para sua pergunta real seria, grosso modo, "diferidos podem resolver a si mesmos. AFAIK, a teoria por trás de promessas e diferidos vem de [Functional Reactive Programming | haskell.org/haskellwiki/Functional_Reactive_Programming] , que é uma técnica para achatar retornos de chamada. . "
Fncomp
2
Isso é totalmente errado e seus exemplos são igualmente fáceis de fazer com retornos de chamada. As promessas não são sobre agregação e dissociação de retorno de chamada, mas fornecer uma DSL para escrever código assíncrono como o código de sincronização é gravado. Especialmente, fn(callback, errback)não é tão acoplado nem menos útil do que fn().then(callback, errback)- mas essa é uma maneira errada de usar promessas de qualquer maneira. Eu odeio especialmente o $.whenexemplo do culto à carga - não há absolutamente nenhuma razão para que você não possa ter uma $.whenfunção que funcione com retornos de chamada.
Esailija
No entanto, isso não responde à pergunta de que eu poderia saber o que diabos é o retorno de chamada.
Bhojendra Rauniyar
146

Estas respostas, incluindo a resposta selecionada, são bons para a introdução de promessas conceitualmente, mas com falta de detalhes do que exatamente são as diferenças na terminologia que surge quando usando bibliotecas a sua aplicação (e não são diferenças importantes).

Como ainda é uma especificação em evolução , atualmente a resposta vem da tentativa de pesquisar tanto as referências (como a wikipedia ) quanto as implementações (como o jQuery ):

  • Adiado : nunca descrito em referências populares, 1 2 3 4, mas comumente usado pelas implementações como o árbitro da resolução da promessa (implementação e ). 5 6 7 resolvereject

    Às vezes, adiamentos também são promessas (implementação then), 5 6 vezes, é mais puro ter o adiado apenas com capacidade de resolução e forçar o usuário a acessar a promessa de uso . 7 then

  • Promessa : a palavra mais abrangente para a estratégia em discussão.

    Um objeto proxy que armazena o resultado de uma função de destino cuja sincronicidade gostaríamos de abstrair, além de expor uma thenfunção que aceita outra função de destino e retorna uma nova promessa. 2

    Exemplo do CommonJS :

    > asyncComputeTheAnswerToEverything()
        .then(addTwo)
        .then(printResult);
    44

     

    Sempre descrito em referências populares, embora nunca especificado sobre cuja resolução de responsabilidade se enquadra. 1 2 3 4

    Sempre presente em implementações populares e nunca com capacidade de resolução. 5 6 7

  • Futuro : um termo aparentemente obsoleto encontrado em algumas referências populares 1 e pelo menos em uma implementação popular 8 , mas aparentemente sendo eliminado da discussão em preferência ao termo 'promessa' 3 e nem sempre mencionado nas introduções populares ao tópico. 9

    No entanto, pelo menos uma biblioteca usa o termo genericamente para abstrair a sincronicidade e o tratamento de erros, sem fornecer thenfuncionalidade. 10 Não está claro se evitar o termo 'promessa' foi intencional, mas provavelmente é uma boa escolha, pois as promessas são construídas em torno de 'partes do mundo'. 2

Referências

  1. Wikipedia sobre promessas e futuros
  2. Promessas / especificações A +
  3. Padrão DOM sobre promessas
  4. Padrão DOM promete WIP de especificação
  5. Adiamento do Kit de Ferramentas DOJO
  6. Adiado jQuery
  7. Q
  8. FutureJS
  9. Seção Javascript funcional no Promises
  10. Futuros no teste de integração do AngularJS

Coisas potencialmente confusas

Woahdae
fonte
5
Para adicionar um pouco mais de esclarecimento sobre o termo "Futuro" - os futuros têm uma longa história em muitas linguagens de programação que datam de meados dos anos 80. E o termo ainda é amplamente utilizado hoje, principalmente na JVM. O JavaScript parece ter escolhido usar o termo "Promessa" para significar algo semelhante ao que Java quer dizer com "Futuro". Scala separa o mesmo conceito em "Futuro" e "Promessa" para se referir ao identificador "ler" e ao identificador "escrever" do que os programadores JavaScript chamam de Promessa.
Heather Miller
1
E, claro, a Microsoft teve que vir para cima com seu próprio termo para isso, então em C # são chamadosTask
BlueRaja - Danny Pflughoeft
72

O que realmente fez tudo clicar para mim foi esta apresentação de Domenic Denicola.

Em uma essência do github , ele deu a descrição que eu mais gosto, é muito conciso:

O objetivo das promessas é devolver a composição funcional e os erros que ocorrem no mundo assíncrono.

Em outras palavras, as promessas são uma maneira que nos permite escrever código assíncrono que é quase tão fácil de escrever quanto se fosse síncrono .

Considere este exemplo, com promessas:

getTweetsFor("domenic") // promise-returning async function
    .then(function (tweets) {
        var shortUrls = parseTweetsForUrls(tweets);
        var mostRecentShortUrl = shortUrls[0];
        return expandUrlUsingTwitterApi(mostRecentShortUrl); // promise-returning async function
    })
    .then(doHttpRequest) // promise-returning async function
    .then(
        function (responseBody) {
            console.log("Most recent link text:", responseBody);
        },
        function (error) {
            console.error("Error with the twitterverse:", error);
        }
    );

Funciona como se você estivesse escrevendo este código síncrono:

try {
    var tweets = getTweetsFor("domenic"); // blocking
    var shortUrls = parseTweetsForUrls(tweets);
    var mostRecentShortUrl = shortUrls[0];
    var responseBody = doHttpRequest(expandUrlUsingTwitterApi(mostRecentShortUrl)); // blocking x 2
    console.log("Most recent link text:", responseBody);
} catch (error) {
    console.error("Error with the twitterverse: ", error);
}

(Se isso ainda parecer complicado, assista à apresentação!)

Em relação ao diferido, é um caminho .resolve()ou .reject()promessas. Nas especificações Promises / B , é chamado .defer(). No jQuery, é $.Deferred().

Observe que, até onde eu sei, a implementação do Promise no jQuery está quebrada (veja a essência), pelo menos a partir do jQuery 1.8.2.
Ele supostamente implementa as tabelas Promises / A , mas você não recebe o tratamento correto de erros que deveria, no sentido de que toda a funcionalidade "assíncrona try / catch" não funcionará. O que é uma pena, porque ter um "try / catch" com código assíncrono é absolutamente legal.

Se você vai usar o Promises (você deve testá-lo com seu próprio código!), Use o Q de Kris Kowal . A versão do jQuery é apenas um agregador de retorno de chamada para escrever um código jQuery mais limpo, mas perde o objetivo.

Em relação ao futuro, não faço ideia, não vi isso em nenhuma API.

Edit: Domenic Denicola fala no youtube sobre Promessas a partir do comentário de @Farm abaixo.

Uma citação de Michael Jackson (sim, Michael Jackson ) do vídeo:

Quero que você grave esta frase em sua mente: uma promessa é um valor assíncrono .

Esta é uma descrição excelente: uma promessa é como uma variável do futuro - uma referência de primeira classe a algo que, em algum momento, existirá (ou acontecerá).

Camilo Martin
fonte
5
A grande explicação de Futuros (! Agora implementado no DOM) por um membro da equipe principal W3 e Chrome pode ser encontrada aqui: xanthir.com/b4PY0
oligofren
1
@ oligofren Obrigado pelo link, isso parece bom! A propósito, que favicon misteriosamente irritante lol.
Camilo Martin
1
Esta resposta precisa de muito mais votos. Deve ser votado acima da resposta aceita pela IMO.
quer
1
Palestra do Domenic Denicola no YouTube sobre Promessas: youtube.com/watch?v=hf1T_AONQJU
Farm
@Farm Great! Vou acrescentar isso à resposta.
Camilo Martin,
32

Uma promessa representa um proxy para um valor não necessariamente conhecido quando a promessa é criada. Ele permite associar manipuladores ao eventual valor de sucesso ou motivo de falha de uma ação assíncrona. Isso permite que métodos assíncronos retornem valores como métodos síncronos: em vez do valor final, o método assíncrono retorna a promessa de ter um valor em algum momento no futuro.

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

O deferred.promise()método permite que uma função assíncrona impeça que outro código interfira no andamento ou status de sua solicitação interna. A Promessa expõe apenas os métodos Adiados necessários para anexar manipuladores adicionais ou determinar o estado ( então, concluído, falhar, sempre, canalizar, progresso, estado e promessa ), mas não aqueles que alteram o estado ( resolver, rejeitar, notificar, resolver com, rejectWith e notifyWith ).

Se o destino for fornecido, deferred.promise()anexará os métodos a ele e retornará esse objeto em vez de criar um novo. Isso pode ser útil para anexar o comportamento da promessa a um objeto que já existe.

Se você estiver criando um adiado, mantenha uma referência ao adiado para que ele possa ser resolvido ou rejeitado em algum momento. Retorne apenas o objeto Promise via deferred.promise () para que outro código possa registrar retornos de chamada ou inspecionar o estado atual.

Simplesmente, podemos dizer que uma promessa representa um valor que ainda não é conhecido, enquanto que um adiado representa um trabalho que ainda não foi concluído.


insira a descrição da imagem aqui

IRSHAD
fonte
1
mais 1 para a representação do gráfico. Bravisimo !! ^ _ ^
Ashok MA