DEV Community

loading...

Por que e quando usar Promise?

kabrau profile image Marcelo Cabral Ghilardi ・5 min read

Oi Pessoas

Neste post vou explicar por que e quando devemos usar Promise, para isso, dividi nos seguintes tópicos:

Fluxo de Código

Todos devemos saber que boa parte de nosso código JS é assíncrona, principalmente chamadas ao servidor. Isto é, não podemos garantir o fluxo de nosso código.
Por exemplo:

console.log("INICIO");

$.ajax('http://domain/service-1')
    .done(function (data) {
        console.log("chamada 01", data);
    })

$.ajax('http://domain/service-2')
    .done(function (data) {
        console.log("chamada 02", data);
    })

$.ajax('http://domain/service-3')
    .done(function (data) {
        console.log("chamada 03", data);
    })

console.log("FINAL");

No exemplo acima o resultado na tela, após o "INICIO", pode ser qualquer um, deve aparecer algo como:

INICIO
chamada 03
FINAL
chamada 01
chamada 02

Um trecho de código que está localizado após uma função assíncrona pode ser executado antes da mesma ter sido realizada, e quando temos mais de uma função assíncrona, não podemos garantir seu fluxo ordenado.

old-style

Para garantir o fluxo, podemos adotar várias alternativas, uma das alternativas mais usadas é encadear as chamadas, por exemplo:

console.log("INICIO");

$.ajax('http://domain/service-1')
    .done(function (data) {
        console.log("chamada 01", data);

        $.ajax('http://domain/service-2')
            .done(function (data) {
                console.log("chamada 02", data);

                $.ajax('http://domain/service-3')
                    .done(function (data) {
                        console.log("chamada 03", data);

                        console.log("FINAL");
                    })
            })
    })

No exemplo acima, podemos garantir o fluxo, mas o problema, nesta modelo, é que perdemos o poder do assíncrono.

Poder do Assíncrono

O que chamo de Poder do Assíncrono é o fato que quando fazemos chamadas para um servidor, muitas vezes podemos ter vários retornos simultâneos, deixando a leitura de nossa página muito mais rápida.

Veja a imagem abaixa, são 4 chamadas assíncronas ao servidor, a primeira levou 393ms, a segunda 1.53s, a terceira 1.55s e a última 1.51s. O tempo total para as 4 chamadas foi de menos de 2s, isso porque foram chamadas assíncronas.

Imagem assincrona

Agora, neste mesmo exemplo acima, se as chamadas não fossem assíncronas, o tempo total seria de 4.98s, mais que o dobro, este é o Poder do Assíncrono.

Promises

Promise é um método para executar processos assíncronos. Um Promisse possui os estados pendente, realizado, rejeitado, finalizado.

A sintaxe padrão de um Promisse é:

     var novaPromessa = new Promise((resolve, reject) => { ... });

     novaPromessa
        .then( (data) => {...} )
        .catch( (erro) => {...} ) 

Onde:

  • ao criar, a situação equivale a pendente
  • se preencher resolve, a situação equivale a realizado e executa o then
  • se preencher reject, a situação equivale a rejeitado e executa o catch
  • se adicionar mais um then após o catch, a situação equivale a finalizado, podendo adicionar then e catch encadeados.

Vamos para o primeiro exemplo, no exemplo abaixo, simulamos a chamada de leitura de clientes (poderia ser uma chamada $ajax ou fetch), o retorno da chamada atribuímos a resolve, neste caso o método em then recebe os dados informados no resolve.

console.log("Inicio");

var processos = new Promise((resolve, reject) => {

    setTimeout(() => {
        console.log("leituraClientes: simulação de chamada em 1 segundo");
        var dadosRetornados = "cliente JOÃO"

        resolve(dadosRetornados);
        //reject("Deu erro");
    }, 1000);

}).then((dados) => {
    console.log("then:",dados);

}).catch((erro) => {
    console.error("catch:", erro);
});

Resultado:

Inicio
leituraClientes: simulação de chamada em 1 segundo
then: cliente JOÃO

Poderia ter dado um erro na chamada, os dados poderiam ser inválidos, para simular podes comentar a linha do resolve e descomentar a linha do reject.

console.log("Inicio");

var processos = new Promise((resolve, reject) => {

    setTimeout(() => {
        console.log("leituraClientes: simulação de chamada em 1 segundo");
        var dadosRetornados = "cliente JOÃO"

        //resolve(dadosRetornados);
        reject("Deu erro");
    }, 1000);

}).then((dados) => {
    console.log("then:",dados);

}).catch((erro) => {
    console.error("catch:", erro);
});

Resultado:

Inicio
leituraClientes: simulação de chamada em 1 segundo
catch: Deu erro

Note que, a informação que colocar no resolve ou reject vai para o then ou catch, respectivamente.

THEN encadeado para tratar os dados

As vezes, ao receber dados do servidor, precisamos tratar os dados, filtrar, modificar, etc.... Neste caso, podes fazer um código enorme, ou quebrar o código em partes.

Esta característica não é do Promise, mas achei importante demonstrar.

No exemplo abaixo, ao receber os dados da chamada, passa por vários *then*s até finalizar todo processo.

console.log("Inicio");

var erro ;
//erro = true;

var leituraClientes = new Promise((resolve, reject) => {

    console.log("L01: Buscando clientes");
    dados = "CLIENTE 01; CLIENTE 02; CLIENTE 03";

    setTimeout(() => {

        if (erro)
        reject("DEU ERRO");

        console.log("Dados recebidos: ", dados);

        resolve(dados);
    }, 1000);

}).then((dados) => {
    console.log("");
    console.log("L02 toLower");
    console.log("Dados recebidos: ", dados);

    dados = dados.toLowerCase();
    console.log("Dados enviados: ", dados);

    return dados


}).then((dados) => {

    console.log("");
    console.log("L03 split");
    console.log("Dados recebidos: ", dados);

    dados = dados.split(";")
    console.log("Dados enviados: ", dados);

    return dados

}).then((dados) => {

    console.log("");
    console.log("Resultado Final");
    console.log("Dados recebidos: ", dados);

}).catch((erro) => {
    console.error("ERRO:", erro);
});

Resultado

Inicio
L01: Buscando clientes
Dados recebidos:  CLIENTE 01; CLIENTE 02; CLIENTE 03

L02 toLower
Dados recebidos:  CLIENTE 01; CLIENTE 02; CLIENTE 03
Dados enviados:  cliente 01; cliente 02; cliente 03

L03 split
Dados recebidos:  cliente 01; cliente 02; cliente 03
Dados enviados:  [ 'cliente 01', ' cliente 02', ' cliente 03' ]

Resultado Final
Dados recebidos:  [ 'cliente 01', ' cliente 02', ' cliente 03' ]

Você pode simular um erro descomentando a linha com o texto: //erro = true;. O resultado será:

Inicio
L01: Buscando clientes
Dados recebidos:  CLIENTE 01; CLIENTE 02; CLIENTE 03
ERRO: DEU ERRO

O Poder do assíncrono com Promise

Esta é a parte mais importante deste post, se temos várias chamadas, queremos fazê-las de forma assíncrona, mas ao mesmo tempo, preciso garantir que todas foram finalizadas antes de iniciar um processo (uma tela por exemplo).

Para isso podemos usar o Promisse.all, este método garante que só vai executar o then se todas as chamadas estiverem finalizadas e sem erros. A sintaxe é:

Promise.all([chamadas,...]).then( (retornos) => {...} )*.

A forma de usar é criar todos os Promises necessários, e chamá-los todos a partir do método, exemplo de código:

var clientes = new Promise((resolve, reject) => { resolve("clientes") })
var departamentos = new Promise((resolve, reject) => { resolve("deptos") })
var equipes = new Promise((resolve, reject) => { resolve("equipes") })
var teste = new Promise((resolve, reject) => { resolve("teste") })

Promise.all([clientes, departamentos, equipes, teste]).then( (values) => {
    console.log("Resultados:", values);
}).catch((erro) => {    
    console.error("ERRO", erro)
})

No exemplo acima, após todas as chamadas terminarem, os valores de retornos das chamadas são atribuídas à values no método then do Promise.all. Caso algum método retornasse erro, executaria o catch do Promise.all

Também existe o Promise.race, é exatamente uma corrida entre as chamadas. A primeira chamada que resolver ou rejeitar ganha a corrida, e as demais são ignoradas.

Fonte: https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Promise

Repositório com os códigos: https://github.com/kabrau/publications/tree/master/Promises

Discussion (0)

pic
Editor guide