O que a função then () significa em JavaScript?

275

Eu tenho visto um código que se parece com:

myObj.doSome("task").then(function(env) {
    // logic
});

De onde then()vem?

Kay Pale
fonte
8
ATUALIZADO: Descobri que isso tem a ver com a API de promessas do CommonJS sitepen.com/blog/2010/01/19/…
Kay Pale

Respostas:

349

A maneira tradicional de lidar com chamadas assíncronas em JavaScript tem sido com retornos de chamada. Digamos que tivemos que fazer três chamadas para o servidor, uma após a outra, para configurar nosso aplicativo. Com retornos de chamada, o código pode se parecer com o seguinte (assumindo uma função xhrGET para fazer a chamada do servidor):

// Fetch some server configuration
    xhrGET('/api/server-config', function(config) {
        // Fetch the user information, if he's logged in
        xhrGET('/api/' + config.USER_END_POINT, function(user) {
            // Fetch the items for the user
            xhrGET('/api/' + user.id + '/items', function(items) {
                // Actually display the items here
            });
        });
    });

Neste exemplo, buscamos primeiro a configuração do servidor. Com base nisso, buscamos informações sobre o usuário atual e, finalmente, obtemos a lista de itens para o usuário atual. Cada chamada xhrGET utiliza uma função de retorno de chamada que é executada quando o servidor responde.

Agora, é claro, quanto mais níveis de aninhamento tivermos, mais difícil será o código de ler, depurar, manter, atualizar e basicamente trabalhar. Isso geralmente é conhecido como inferno de retorno de chamada. Além disso, se precisássemos manipular erros, possivelmente passamos outra função para cada chamada xhrGET para dizer o que ela precisa fazer em caso de erro. Se quisermos ter apenas um manipulador de erro comum, isso não é possível.

A API Promise foi projetada para resolver esse problema de aninhamento e o tratamento de erros.

A API Promise propõe o seguinte:

  1. Cada tarefa assíncrona retornará um promiseobjeto.
  2. Cada promiseobjeto terá uma thenfunção que pode receber dois argumentos, um success manipulador e umerror manipulador.
  3. O sucesso ou o manipulador de erros na thenfunção será chamado apenas uma vez , após a conclusão da tarefa assíncrona.
  4. A thenfunção também retornará a promise, para permitir o encadeamento de várias chamadas.
  5. Cada manipulador (sucesso ou erro) pode retornar a value, que será passado para a próxima função como um argument, na cadeia de promises.
  6. Se um manipulador retornar a promise(fizer outra solicitação assíncrona), o próximo manipulador (êxito ou erro) será chamado somente depois que a solicitação for concluída.

Portanto, o código de exemplo anterior pode ser traduzido para algo como o seguinte, usando promessas e o $httpserviço (em AngularJs):

$http.get('/api/server-config').then(
    function(configResponse) {
        return $http.get('/api/' + configResponse.data.USER_END_POINT);
    }
).then(
    function(userResponse) {
        return $http.get('/api/' + userResponse.data.id + '/items');
    }
).then(
    function(itemResponse) {
        // Display items here
    }, 
    function(error) {
        // Common error handling
    }
);

Propagando Sucesso e Erro

As promessas de encadeamento são uma técnica muito poderosa que nos permite realizar muitas funcionalidades, como fazer com que um serviço faça uma chamada ao servidor, faça algum pós-processamento dos dados e depois devolva os dados processados ​​ao controlador. Mas quando trabalhamos com promise correntes, há algumas coisas que precisamos ter em mente.

Considere a seguinte promisecadeia hipotética com três promessas, P1, P2 e P3. Cada promiseum tem um manipulador de sucesso e um manipulador de erros; portanto, S1 e E1 para P1, S2 e E2 para P2 e S3 e E3 para P3:

xhrCall()
  .then(S1, E1) //P1
  .then(S2, E2) //P2
  .then(S3, E3) //P3

No fluxo normal de coisas, onde não há erros, o aplicativo fluiria através de S1, S2 e, finalmente, S3. Mas na vida real, as coisas nunca são tão fáceis. P1 pode encontrar um erro ou P2 pode encontrar um erro, acionando E1 ou E2.

Considere os seguintes casos:

• Recebemos uma resposta bem-sucedida do servidor em P1, mas os dados retornados não estão corretos ou não há dados disponíveis no servidor (pense na matriz vazia). Nesse caso, para a próxima promessa P2, ele deve acionar o manipulador de erros E2.

• Recebemos um erro pela promessa P2, acionando E2. Porém, dentro do manipulador, temos dados do cache, garantindo que o aplicativo possa carregar normalmente. Nesse caso, podemos querer garantir que após E2, S3 seja chamado.

Portanto, toda vez que escrevemos um manipulador de sucesso ou erro, precisamos fazer uma chamada - dada nossa função atual, essa promessa é um sucesso ou um fracasso para o próximo manipulador na cadeia de promessas?

Se quisermos acionar o manipulador de sucesso para a próxima promessa na cadeia, podemos apenas retornar um valor do manipulador de sucesso ou erro

Se, por outro lado, queremos acionar o manipulador de erros para a próxima promessa na cadeia, podemos fazer isso usando um deferredobjeto e chamando seu reject()método

Agora, o que é objeto adiado?

Objetos adiados no jQuery representam uma unidade de trabalho que será concluída posteriormente, geralmente de forma assíncrona. Depois que a unidade de trabalho é concluída, o deferredobjeto pode ser definido como resolvido ou com falha.

Um deferredobjeto contém um promiseobjeto. Através do promiseobjeto, você pode especificar o que acontecerá quando a unidade de trabalho for concluída. Você faz isso definindo funções de retorno de chamada no promiseobjeto.

Objetos adiados no Jquery: https://api.jquery.com/jquery.deferred/

Objetos adiados em AngularJs: https://docs.angularjs.org/api/ng/service/ $ q

Sid
fonte
3
Muito bem escrito. Isso me ajudou a realmente cumprir promessas.
Ju66ernaut
O manipulador de erros, o segundo parâmetro, é sempre opcional?
1,21 gigawatts
Esta é de longe a melhor resposta que já vi até agora!
Imam Bux
78

a função then () está relacionada a "promessas de Javascript" usadas em algumas bibliotecas ou estruturas como jQuery ou AngularJS.

Uma promessa é um padrão para lidar com operações assíncronas. A promessa permite que você chame um método chamado "then" que permite especificar as funções a serem usadas como retornos de chamada.

Para mais informações, consulte: http://wildermuth.com/2013/8/3/JavaScript_Promises

E para promessas Angular: http://liamkaufman.com/blog/2013/09/09/using-angularjs-promises/

almoraleslopez
fonte
4
então é como um retorno de chamada que é executado quando a tarefa é concluída? Como é que é diferente
Muhammad Umer
3
o JavaScript Promises no outro comentário diz A promise can only succeed or fail onceIf a promise has succeeded or failed and you later add a success/failure callback, the correct callback will be called
:,
Além disso, os nuggets Promessa explica como usar promiseeo que seria feito comcallback
Xiao
Na primeira página, há pedaços de código ausentes (grandes espaços em branco em branco). A maioria das pessoas pensa em inspecionar o elemento e encontra os URLs do violino embaixo. Esta mensagem é para o resto - ainda mexe trabalho;)
DanteTheSmith
1
@MuhammadUmer: ler este stackoverflow.com/a/31453579/1350476 (resposta por Sid)
SharpCoder
32

Que eu saiba, não existe um then()método interno javascript(no momento da redação deste artigo).

Parece que o que quer que doSome("task")esteja retornando tem um método chamado then.

Se você registrar o resultado do retorno doSome()no console, poderá ver as propriedades do que foi retornado.

console.log( myObj.doSome("task") ); // Expand the returned object in the
                                     //   console to see its properties.

ATUALIZAÇÃO (A partir de ECMAScript6) : -

o .then() função foi incluída no javascript puro.

Na documentação do Mozilla aqui ,

O método then () retorna uma promessa. São necessários dois argumentos: funções de retorno de chamada para os casos de sucesso e falha da Promessa.

O objeto Promise, por sua vez, é definido como

O objeto Promise é usado para cálculos adiados e assíncronos. Uma promessa representa uma operação que ainda não foi concluída, mas é esperada no futuro.

Ou seja, Promiseatua como um espaço reservado para um valor que ainda não foi computado, mas deve ser resolvido no futuro. E a .then()função é usada para associar as funções a serem invocadas no Promise quando for resolvido - como um sucesso ou um fracasso.

user113716
fonte
12
Não havia um built-in .thennaquela época, mas promessas nativas estão chegando agora em ES6: html5rocks.com/en/tutorials/es6/promises
janfoeh
obrigado por esta resposta, eu estava esperando um retorno de chamada interessante, mas acabou sendo uma função real chamada 'then' que foi retornada.
Spartikus 02/05/19
15

Aqui está uma coisa que fiz para esclarecer como as coisas funcionam. Eu acho que outros também podem achar este exemplo concreto útil:

doit().then(function() { log('Now finally done!') });
log('---- But notice where this ends up!');

// For pedagogical reasons I originally wrote the following doit()-function so that 
// it was clear that it is a promise. That way wasn't really a normal way to do 
// it though, and therefore Slikts edited my answer. I therefore now want to remind 
// you here that the return value of the following function is a promise, because 
// it is an async function (every async function returns a promise). 
async function doit() {
  log('Calling someTimeConsumingThing');
  await someTimeConsumingThing();
  log('Ready with someTimeConsumingThing');
}

function someTimeConsumingThing() {
  return new Promise(function(resolve,reject) {
    setTimeout(resolve, 2000);
  })
}

function log(txt) {
  document.getElementById('msg').innerHTML += txt + '<br>'
}
<div id='msg'></div>

Magnus
fonte
5

Aqui está um pequeno JS_Fiddle.

então é uma pilha de retorno de chamada de método que está disponível após a promessa ser resolvida, faz parte de uma biblioteca como o jQuery, mas agora está disponível em JavaScript nativo e abaixo está a explicação detalhada de como funciona

Você pode fazer uma promessa em JavaScript nativo: assim como existem promessas no jQuery, todas as promessas podem ser empilhadas e, em seguida, podem ser chamadas com Resolver e Rejeitar retornos de chamada. É assim que você pode encadear chamadas assíncronas.

Eu bifurquei e editei do MSDN Docs on Status de carregamento da bateria.

O que isso faz é tentar descobrir se o laptop ou dispositivo do usuário está carregando a bateria. então é chamado e você pode fazer o seu trabalho com êxito.

navigator
    .getBattery()
    .then(function(battery) {
       var charging = battery.charging;
       alert(charging);
    })
    .then(function(){alert("YeoMan : SINGH is King !!");});

Outro exemplo es6

function fetchAsync (url, timeout, onData, onError) {
    
}
let fetchPromised = (url, timeout) => {
    return new Promise((resolve, reject) => {
        fetchAsync(url, timeout, resolve, reject)
    })
}
Promise.all([
    fetchPromised("http://backend/foo.txt", 500),
    fetchPromised("http://backend/bar.txt", 500),
    fetchPromised("http://backend/baz.txt", 500)
]).then((data) => {
    let [ foo, bar, baz ] = data
    console.log(`success: foo=${foo} bar=${bar} baz=${baz}`)
}, (err) => {
    console.log(`error: ${err}`)
})

Definition :: then é um método usado para resolver retornos de chamada assíncronos

isso é introduzido no ES6

Encontre a documentação adequada aqui Es6 Promises

Tarandeep Singh
fonte
Sua resposta não responde de fato à pergunta. Ele fornece apenas um exemplo do uso da API sem explicar de onde thenvem e como funciona. Você deve melhorar sua resposta para fornecer esses detalhes.
Didier L
@TarandeepSingh - em primeiro lugar, em que você alerta o status da bateria e nenhum objeto de promessa é retornado. Então o que é o uso da segunda seguida
Mohit Jain
@MohitJain Ele mostra que você pode fazer vários retornos de chamada, mesmo que não tenha nenhuma nova promessa. Desde então, as várias chamadas também podem ser feitas com Promise.all.
Tarandeep Singh
O que você quer dizer com " pilha de retorno de chamada de método "?
Bergi 17/11/19
4

Eu suspeito que doSome retorna isso, que é myObj, que também possui um método then. Encadeamento de método padrão ...

se doSome não está retornando isso, sendo o objeto no qual doSome foi executado, tenha certeza de que está retornando algum objeto com um método then ...

como aponta @patrick, não existe then () para js padrão

hvgotcodes
fonte
1
Eu suspeito doSome retorna este - nada impõe / justifique tal suspitions
Salatiel Genese
1

doSome ("task") deve estar retornando um objeto de promessa, e essa promessa sempre tem uma função then. Portanto, seu código é assim

promise.then(function(env) {
    // logic
}); 

e você sabe que essa é apenas uma chamada comum para a função de membro.

Hardeep SINGH
fonte
1

.then retorna uma promessa na função assíncrona.

Um bom exemplo seria:

var doSome = new Promise(function(resolve, reject){
    resolve('I am doing something');
});

doSome.then(function(value){
    console.log(value);
});

Para adicionar outra lógica a ela, você também pode adicionar a reject('I am the rejected param')chamada de função e console.log.

appdesigns
fonte
0

Nesse caso, then()é um método de classe do objeto retornado pelo doSome()método


fonte
0

A função ".then ()" é wideley usada para objetos prometidos na programação Asynchoronus para aplicativos da Windows 8 Store. Tanto quanto eu entendi, funciona de alguma forma como um retorno de chamada.

Encontre detalhes nesta documentação http://msdn.microsoft.com/en-us/library/windows/apps/hh700330.aspx

De causa também pode ser o nome para qualquer outra função definida.

user2508620
fonte
0

Outro exemplo:

new Promise(function(ok) {
   ok( 
      /* myFunc1(param1, param2, ..) */
   )
}).then(function(){
     /* myFunc1 succeed */
     /* Launch something else */
     /* console.log(whateverparam1) */
     /* myFunc2(whateverparam1, otherparam, ..) */
}).then(function(){
     /* myFunc2 succeed */
     /* Launch something else */
     /* myFunc3(whatever38, ..) */
})

A mesma lógica usando as funções de seta abreviada:

new Promise((ok) =>
   ok( 
      /* myFunc1(param1, param2, ..) */
)).then(() =>
     /* myFunc1 succeed */
     /* Launch something else */
     /* Only ONE call or statment can be made inside arrow functions */
     /* For example, using console.log here will break everything */
     /* myFunc2(whateverparam1, otherparam, ..) */
).then(() =>
     /* myFunc2 succeed */
     /* Launch something else */
     /* Only ONE call or statment can be made inside arrow functions */
     /* For example, using console.log here will break everything */
     /* myFunc3(whatever38, ..) */
)

NVRM
fonte
-4

Estou com cerca de 8 anos de atraso, bem ... de qualquer forma, eu realmente não sei o que então () faz, mas talvez a MDN possa ter uma resposta. Na verdade, eu posso entender um pouco mais.

Isso mostrará todas as informações (espero) que você precisar. A menos que alguém já tenha postado este link. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then

O formato é promessa.protótipo.then () A promessa e o protótipo são como variáveis ​​semelhantes, mas não como variáveis ​​em javascript, quero dizer, como outras coisas, como navigator.getBattery (). Then (), onde este existe realmente, mas é pouco usado na web, este mostra status sobre a bateria do dispositivo, mais informações e mais sobre o MDN, se você estiver curioso.

Jonathan J. Pecany
fonte