Por que e quando usar Promise?
Tags: javascript, webdev, beginners, fullstack
Published: 2020-03-30 21:15:02

Oi Pessoas

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

* [Fluxo de código](#fluxo)
* [old-style](#old-style)
* [Poder do Assíncrono] (#assincrono)
* [Promises] (#promises)
* [THEN encadeado para tratar os dados] (#thens)
* [O Poder do assíncrono com Promise] (#multiplas)

#### Fluxo de Código <a name="fluxo"></a>

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:

```javascript
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:

```javascript
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 <a name="old-style"></a>

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

```javascript
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 <a name="assincrono"></a>

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](https://github.com/kabrau/publications/raw/master/Promises/images/assincrono.png)

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 <a name="promises"></a>

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 é:
```javascript
     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**.

```javascript
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:
```javascript
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*.

```javascript
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:
```javascript
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 <a name="thens"></a>

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.

```javascript
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
```javascript
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á:

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

#### O Poder do assíncrono com Promise <a name="multiplas"></a>

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 é:

```javascript
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:

```javascript
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