Entendendo JavaScript: Promises
Tags: ptbr, javascript
Published: 2019-09-17 17:13:46

No início da programação em JavaScript as coisas geralmente vão bem: é fácil entender a sintaxe da linguagem e assimilar a outras linguagens popularmente conhecidas, como Python e Java. Ao entrar mais a fundo nas possibilidades de implementação com JavaScript, surge o "bicho papão" para todo iniciante: as Promises.

![Bicho Papão](https://media.giphy.com/media/gh6emBSF3auzxzQamc/giphy.gif)

Entretanto, embora pareçam assustadoras, as Promises são essenciais enquanto programando JavaScript e, com um pouco de prática, passam a ser rotina. Neste post, discutiremos o que são Promises, como utilizá-las, além de um plus pro async/await.

## Antes de tudo, assíncrono

Diferente de algumas outras linguagens, como Python e Java, JavaScript permite a programação de forma **assíncrona**, isto é, as linhas de código de uma função podem não ser executadas uma após a outra: as linhas 1, 2 e 3 serem executadas uma após a outra não é garantia de que a linha 4 será a próxima a ser executada.

![Síncrono vs Assíncrono](https://media.giphy.com/media/tn8zWeNYA73G0/source.gif)


## Fetch

Durante o restante desse tutorial utilizaremos o `fetch` como base para os exemplos. Agora nativo no JavaScript, a API do fetch nos permite fazer requisições HTTP sem utilizar libs externas. Sumarizando bastante, as requisições são pedidos de informação de fontes externas à nossa aplicação, seja de um servidor, uma API, ou outras fontes, e o `fetch` será a função que nos permite solicitar essa informação no nosso código. 

Além do `fetch`, existem libs que permitem fazer requisições HTTP: um exemplo forte na comunidade é a [axios](https://github.com/axios/axios/). Alguns frameworks, como o Angular.js, possuem funções internas ([$http](https://docs.angularjs.org/api/ng/service/$http), no caso do Angular.js) que permitem fazer essas requisições.

## O que são Promises?

Agora sim, Promises. Como explicado na sessão anterior, as requisições buscam informação em fontes externas. Entretanto, o nome *fetch* não foi escolhido ao acaso: pedir informação de uma fonte externa é como jogar uma bola para o cachorro ir buscar — em inglês, a expressão *go fetch* é usada no lugar do **pega**, que é geralmente usado aqui no Brasil — mas, assim como o cachorro demora para trazer a bola de volta, a informação também demora a chegar do servidor.

![Go fetch](https://media.giphy.com/media/klPeFHrWqzPDW/giphy.gif)

Mas, JavaScript é assíncrono: se a linha 3 demorar demais, como impedir que a linha 4 execute? As Promises surgem então nesse contexto: no nosso código, uma Promise (promessa, em português) serve para impedir que a linha 4 execute antes da linha 3, pois era necessária a informação adquirida na linha 3 antes de executar a linha 4. A semântica básica das promises é `do this ... then do that` — `faça isso ... depois faça aquilo`.

Infelizmente, assim como na vida real, nem toda promessa é cumprida, e precisamos nos precaver para esse tipo de situação. A semântica, nesse caso, lembra um pouco o `try catch` de Java: se não é possível recuperar a informação ou houve erro no código do `then`, caímos num `catch`.

<figure class="image">
  <img src="https://boygeniusreport.files.wordpress.com/2016/11/obama-trump-finger-cross-photoshop.jpg?quality=98&strip=all" alt="Quebrando promessas">
  <figcaption>Obama cruzando os dedos: Promises também podem ser quebradas! Disclaimer: essa foto é uma montagem</figcaption>
</figure>

Mesmo que o código dentro do nosso `then` não cause erros, erros lançados pelo servidor e erros na comunicação precisam ser tratados, por exemplo: quando o serviço externo no qual se está buscando a informação não está online, nossa requisição gera um erro de *timeout*; se não estamos conectados à internet no momento em que a requisição é feita, nossa requisição gera um erro de *network*. Detalhes como esse precisam ser tratados, e muitas vezes é isso que transforma as promises em algo delicado.

## Exemplos de Promise

Para fins de exemplo, nosso serviço externo será o [Laguinho](https://laguinho.opendevufcg.org/) da [OpenDevUFCG](https://github.com/OpenDevUFCG).

Quando fizermos uma requisição **GET** para o Laguinho, ele deve nos retornar `{"status":"running"}`. Assim, utilizando o fetch, vejamos o código necessário para fazer essa requisição:

```javascript
const url = 'https://laguinho.opendevufcg.org/';
const resposta = fetch(url);
console.log('Resposta:', resposta);
```

Quando executamos o código acima, o `console.log` nos retorna um objeto do tipo `Promise`, e não o objeto com o status, que deveria ser o retorno. Vamos aplicar a semântica de promises ao código:

```javascript
const url = 'https://laguinho.opendevufcg.org/';
fetch(url).then((resposta) => {
  console.log('Resposta:', resposta);
});
```

Temos então um objeto do tipo `Response`, e a partir dele é possível obter os dados da resposta dada pelo serviço externo. Já que o Laguinho nos retorna um objeto em JSON, aplicaremos a função `json` na resposta para obter os dados. Note que a função `json` também é uma promise.

```javascript
const url = 'https://laguinho.opendevufcg.org/';
fetch(url).then((resposta) => {
  console.log('Resposta:', resposta);
  resposta.json().then((respostaDoServidor) => {
    console.log('Resposta do Servidor:', respostaDoServidor);
  });
});
```

Até agora, todos os nossos casos funcionam. Mas, e se houvéssemos escrito a url incorreta? Como provavelmente não haveria um servidor que se comunica utilizando JSON na url que (por exemplo) trocamos uma letra, a requisição iria falhar. Nesse caso, precisamos de um catch, para impedir que seja lançada uma exceção, e podermos tratar de acordo. Vejamos:

```javascript
const url = 'https://laguinoh.opendevufcg.org/';
fetch(url).then((resposta) => {
  console.log('Resposta:', resposta);
  resposta.json().then((respostaDoServidor) => {
    console.log('Resposta do Servidor:', respostaDoServidor);
  });
}).catch((erro) => {
  console.log('Aconteceu um erro durante a requisição:', erro);
});
```

Agora você já deve ter entendido o que são promises, como elas aparecem no seu código, e como lidar com elas. Mas, em termos de legibilidade de código, ainda há um problema: se existem diversas promises que estão uma dentro da outra, torna-se muito difícil entender o fluxo de execução. Vejamos então uma forma diferente de escrever Promises.

## Plus: Async/Await

![Os mimos](https://github.com/boyEstrogen/Anime-Girls-Holding-Programming-Books/blob/master/Javascript/Doma_Umaru_Java_Script_The_Good_Parts.png?raw=true)

Agora, os mimos. Async/await é uma forma sensacional e bastante legível de escrever Promises, pois além de não ser verborrágica, não torna o código ilegível, seja pelo fluxo ou pelas indentações, mas é necessário encapsular o código em uma função. Vejamos os códigos anteriores quando escrito usando async/await:

```javascript
const url = 'https://laguinho.opendevufcg.org/';
const fluxo = async () => {
  try {
    const resposta = await fetch(url);
    const respostaServidor = await resposta.json();
    console.log('resposta do servidor', respostaServidor);
  } catch(erro) {
    console.log('Aconteceu um erro durante a requisição:', erro);
  }
};
```

Note que, não importa em que linha de código o erro aconteceu, ele sempre cairá no bloco do `catch`. Se você não gostou da ideia de ter um bloco `try/catch` no seu código, saiba que você não é o único: eu costumo escrever as promises utilizando uma mistura dos dois estilos, por exemplo:

```javascript
const url = 'https://laguinho.opendevufcg.org/';
const fluxo = async () => {
  const resposta = await fetch(url).catch((erro) => {
    console.log('Erro no fetch', erro);
  });
  const respostaServidor = await resposta.json().catch((erro) => {
    console.log('Erro no .json', erro);
  });
  console.log('resposta do servidor', respostaServidor);
};
```
Async/Await está se tornando cada vez mais popular, mas o estilo não agrada a todos: fica a seu critério decidir entre usar ou não :)

## Chegamos ao fim desse post... :(

Agradeço bastante pela leitura e sinta-se à vontade para tirar quaisquer dúvidas que ainda existam. Se esse tutorial foi útil, dá uma olhada no meu [outro post](https://dev.to/opendevufcg/testes-em-javascript-conceitos-iniciais-1okj), introduzindo conceitos de teste em JavaScript.

Caso queira entrar em contato, só mandar um [Tweet](https://twitter.com/juliobguedes)! Te convido a continuar lendo textos meus, confere meu [Medium](https://medium.com/@Juliobguedes/).

Se estiver buscando posts sobre programação em Português, te recomendo dar uma olhada no conteúdo do [OpenDevUFCG](https://dev.to/opendevufcg). Se curtir desenvolvimento OpenSource, aproveita e dá uma olhada no [GitHub](https://github.com/opendevufcg) também!