Compreendendo as promessas de JavaScript

Bom dia, Habr! Apresento a você a tradução do artigo “Entendendo promessas em JavaScript”, de Sukhjinder Arora.



Do autor da tradução: Assim como o próprio autor, espero que o artigo tenha sido útil para você. Por favor, se ela realmente ajudou você a aprender algo novo para si mesmo, não tenha preguiça de ir ao artigo original e agradeça ao autor! Ficarei feliz em seu feedback!

Link para a tradução do artigo sobre JavaScript assíncrono do mesmo autor .

JavaScript é uma linguagem de programação de thread único, o que significa que uma coisa pode ser feita por vez. Antes do ES6, usamos retornos de chamada para gerenciar tarefas assíncronas, como solicitação de rede.

Usando promessas, podemos evitar o "inferno de retorno de chamada" e tornar nosso código mais limpo, mais legível e mais fácil de entender.

Suponha que desejemos obter alguns dados do servidor de forma assíncrona, usando retornos de chamada, faríamos algo assim:

getData(function(x){ console.log(x); getMoreData(x, function(y){ console.log(y); getSomeMoreData(y, function(z){ console.log(z); }); }); }); 

Aqui, solicito alguns dados do servidor usando a função getData () , que recebe dados dentro da função de retorno de chamada. Dentro da função de retorno de chamada, solicito dados adicionais chamando a função getMoreData () , passando os dados anteriores como argumento e assim por diante.

É o que chamamos de "inferno de retorno de chamada", onde cada retorno de chamada é aninhado dentro do outro, e cada retorno de chamada interno depende de seu pai.

Podemos reescrever o snippet acima usando promessas:

 getData() .then((x) => { console.log(x); return getMoreData(x); }) .then((y) => { console.log(y); return getSomeMoreData(y); }) .then((z) => { console.log(z); }); 

Você pode ver o que ficou mais legível do que no primeiro exemplo de retorno de chamada.

O que são promessas?


Uma promessa (promessa) é um objeto que contém o valor futuro de uma operação assíncrona. Por exemplo, se você solicitar alguns dados do servidor, a Promis promete que receberemos esses dados, que poderemos usar no futuro.

Antes de mergulhar em todas essas coisas técnicas, vejamos a terminologia das promessas.

Estados da promessa


Uma promessa em JavaScript, como uma promessa na vida real, tem três estados. Isso pode ser 1) não resolvido (pendente), 2) resolvido / resolvido (concluído) ou 3) rejeitado / rejeitado.



Não resolvido ou pendente - Promis aguarda se o resultado não estiver pronto. Ou seja, espera a conclusão de algo (por exemplo, a conclusão de uma operação assíncrona).
Resolvido ou concluído - Promis resolvido se o resultado estiver disponível. Ou seja, algo concluiu sua execução (por exemplo, uma operação assíncrona) e tudo correu bem.
Rejeitado - Promis rejeitado se ocorrer um erro durante a execução.

Agora que sabemos o que é Promis e sua terminologia, voltemos à parte prática das promessas.

Criar Promis


Na maioria dos casos, você simplesmente usa promessas, não as cria, mas ainda é importante saber como elas são criadas.

Sintaxe:

 const promise = new Promise((resolve, reject) => { ... }); 

Criamos uma nova promessa usando o construtor Promises, é necessário um argumento, um retorno de chamada, também conhecido como função executiva, que recebe 2 retornos de chamada, resolve e rejeita .

A função executiva é executada imediatamente após a criação da promessa. Uma promessa é feita chamando resolve () e rejeitada por rejeitar () . Por exemplo:

 const promise = new Promise((resolve, reject) => { if(allWentWell) { resolve('  !'); } else { reject('-   '); } }); 

resolve () e rejeita () usam um argumento, que pode ser uma sequência, um número, uma expressão lógica, uma matriz ou um objeto.

Vamos dar uma olhada em outro exemplo para entender completamente como as promessas são criadas.

 const promise = new Promise((resolve, reject) => { const randomNumber = Math.random(); setTimeout(() => { if(randomNumber < .6) { resolve('  !'); } else { reject('-   '); } }, 2000); }); 

Aqui, criei uma nova promessa usando o construtor Promis. Uma promessa é executada ou rejeitada 2 segundos após a sua criação. Uma promessa é executada se randomNumber for menor que 0,6 e rejeitada em outros casos.

Quando uma promessa for criada, ela ficará pendente e seu valor será indefinido .


Após 2 segundos, o cronômetro termina, a promessa é executada ou rejeitada aleatoriamente e seu valor será aquele passado para a função de resolução ou rejeição . Abaixo está um exemplo de dois casos:

Conclusão bem sucedida:



Promessa de rejeição:



Nota: A promessa pode ser executada ou rejeitada apenas uma vez. Outras chamadas para resolver () ou rejeitar () não afetarão o estado da promessa de forma alguma. Um exemplo:

 const promise = new Promise((resolve, reject) => { resolve('Promise resolved'); //   reject('Promise rejected'); //       }); 

Desde que resolve () foi chamado primeiro, a promessa agora tem o status "concluído". A chamada subsequente para rejeitar () não afetará o estado da promessa de forma alguma.

Usando Promis


Agora sabemos como criar promessas, agora vamos descobrir como aplicar a promessa já criada. Usamos promessas usando os métodos then () e catch () .

Por exemplo, consultar dados de uma API usando busca , o que retorna uma promessa.

Sintaxe .then () : promessa.then (successCallback, failCallback)

successCallback é chamado se a promessa foi executada com sucesso. É preciso um argumento, que é o valor passado para resolver () .

failCallback é chamado se a promessa foi rejeitada. É preciso um argumento, que é o valor dado para rejeitar () .

Um exemplo:

 const promise = new Promise((resolve, reject) => { const randomNumber = Math.random(); if(randomNumber < .7) { resolve('  !'); } else { reject(new Error('-   ')); } }); promise.then((data) => { console.log(data); //  '  !' }, (error) => { console.log(error); //   } ); 

Se a promessa foi executada, successCallback é chamado com o valor passado para resolve () . E se a promessa foi rejeitada, failCallback é chamado com o valor passado para rejeitar ().

Sintaxe .catch () : promessa.catch (failCallback)

Usamos catch () para lidar com erros. Isso é mais legível do que o tratamento de erros dentro de failCallback dentro do retorno de chamada do método then () .

 const promise = new Promise((resolve, reject) => { reject(new Error('-   ')); }); promise .then((data) => { console.log(data); }) .catch((error) => { console.log(error); //   }); 

Promise Chain


Os métodos then () e catch () também podem retornar uma nova promessa, que pode ser processada por uma cadeia de outros then () no final do método then () anterior.

Usamos uma cadeia de promessas quando queremos concluir uma sequência de promessas.

Por exemplo:

 const promise1 = new Promise((resolve, reject) => { resolve('Promise1 '); }); const promise2 = new Promise((resolve, reject) => { resolve('Promise2 '); }); const promise3 = new Promise((resolve, reject) => { reject('Promise3 '); }); promise1 .then((data) => { console.log(data); // Promise1  return promise2; }) .then((data) => { console.log(data); // Promise2  return promise3; }) .then((data) => { console.log(data); }) .catch((error) => { console.log(error); // Promise3  }); 

Então, o que está acontecendo aqui?


Quando a promessa1 é cumprida, o método then () é chamado , que retorna a promessa2.
Em seguida, quando a promessa2 é cumprida , () é chamada novamente e retorna a promessa3 .

Como promessa3 é rejeitada, em vez do próximo () , catch () é chamado, que lida com a rejeição da promessa3 .

Nota: Como regra, um método catch () é suficiente para lidar com a rejeição de qualquer uma das promessas da cadeia, se esse método estiver no final dele.

Erro comum


Muitos iniciantes cometem um erro investindo algumas promessas dentro de outras. Por exemplo:

 const promise1 = new Promise((resolve, reject) => { resolve('Promise1 '); }); const promise2 = new Promise((resolve, reject) => { resolve('Promise2 '); }); const promise3 = new Promise((resolve, reject) => { reject('Promise3 '); }); promise1.then((data) => { console.log(data); // Promise1  promise2.then((data) => { console.log(data); // Promise2  promise3.then((data) => { console.log(data); }).catch((error) => { console.log(error); // Promise3  }); }).catch((error) => { console.log(error); }) }).catch((error) => { console.log(error); }); 

Embora isso funcione bem, é considerado um estilo ruim e torna o código menos legível. Se você tiver uma sequência de promessas a executar, será melhor colocá-las uma após a outra do que colocar uma dentro da outra.

Promise.all ()


Esse método aceita uma matriz de promessas e retorna uma nova promessa que será executada quando todas as promessas dentro da matriz forem executadas ou rejeitadas assim que uma promessa rejeitada for encontrada. Por exemplo:

 const promise1 = new Promise((resolve, reject) => { setTimeout(() => { resolve('Promise1 '); }, 2000); }); const promise2 = new Promise((resolve, reject) => { setTimeout(() => { resolve('Promise2 '); }, 1500); }); Promise.all([promise1, promise2]) .then((data) => console.log(data[0], data[1])) .catch((error) => console.log(error)); 

Aqui, o argumento dentro de then () é uma matriz que contém os valores das promessas na mesma ordem em que foram passadas para Promise.all () . (Somente se todas as promessas forem executadas)

A promessa é rejeitada com a causa da rejeição da primeira promessa na matriz transferida. Por exemplo:

 const promise1 = new Promise((resolve, reject) => { setTimeout(() => { resolve('Promise1 '); }, 2000); }); const promise2 = new Promise((resolve, reject) => { setTimeout(() => { reject('Promise2 '); }, 1500); }); Promise.all([promise1, promise2]) .then((data) => console.log(data[0], data[1])) .catch((error) => console.log(error)); // Promise2  

Aqui temos duas promessas, onde uma é executada após 2 segundos e a outra se desvia após 1,5 segundos. Assim que a segunda promessa é rejeitada, a promessa retornada de Promise.all () é rejeitada sem aguardar a primeira.

Esse método pode ser útil quando você possui mais de uma promessa e deseja saber quando todas as promessas foram concluídas. Por exemplo, se você solicitar dados de uma API de terceiros e desejar fazer algo com esses dados somente quando todas as solicitações forem bem-sucedidas.

Como resultado, temos Promise.all () , que aguarda a execução bem-sucedida de todas as promessas, ou conclui sua execução quando detecta a primeira falha na matriz de promessas.

Promise.race ()


Esse método aceita uma matriz de promessas e retorna uma nova promessa que será executada assim que a promessa cumprida na matriz for atendida ou rejeitada se a promessa rejeitada ocorrer anteriormente. Por exemplo:

 const promise1 = new Promise((resolve, reject) => { setTimeout(() => { resolve('Promise1 '); }, 1000); }); const promise2 = new Promise((resolve, reject) => { setTimeout(() => { reject('Promise2 '); }, 1500); }); Promise.race([promise1, promise2]) .then((data) => console.log(data)) // Promise1  .catch((error) => console.log(error)); 

Aqui temos duas promessas, onde uma é executada após 1 segundo e a outra se desvia após 1,5 segundos. Assim que a primeira promessa for cumprida, a promessa retornada de Promise.race () terá o status cumprido sem aguardar o status da segunda promessa.

Aqui, os dados que são passados ​​para then () são o valor da primeira promessa executada.

Como resultado, Promise.race () aguarda a primeira promessa e assume seu status como o status da promessa retornada.

Comentário do autor da tradução: Daí o próprio nome. Corrida - Corrida

Conclusão


Aprendemos o que são promessas e o que elas comem em JavaScript. As promessas consistem em duas partes: 1) Crie uma promessa e 2) Use uma promessa. Na maioria das vezes, você usará promessas em vez de criá-las, mas é importante saber como elas são criadas.

Só isso, espero que este artigo tenha sido útil para você!

Source: https://habr.com/ru/post/pt439746/


All Articles