Promeses no JavaScript
Tags: javascript, codequality, tutorial
Published: 2021-09-27 23:04:31

## Fluxo Assíncrono

> O JavaScript é uma linguagem `single-threade`

### Consequências

- Deixa o usuário sem ação durante todo o tempo para a realização de uma operação;
- Não permite que o servidor seja capaz de realizar algum outra requisição até que a atual termine,,,,,,.

### Solução

- Buscar em tornar operações muito extensas em operações assíncronas, dessa forma tornando o código mais performático e bem escrito.

---
## Callbacks

> Assim que uma operação for concluída a callback será executada 
>

### Exemplo:

```
const fs = require('fs');
fs.readFile('./arquivo.txt', (err, content) => {
  if (err) {
    console.error(`Erro ao ler o arquivo: ${err.message}`);
    return; 
  } 
  console.log(`Arquivo lido. Conteúdo: ${content.toString('utf8')}`);});
```
No código acima é utilizado uma `node-style callback` (formato de callback nativa do Node.js)

### O que está acontecendo:

- No primeiro parâmetro é passado o arquivo que sera lido;
- No segundo parâmetro é passado uma callback para tratar a resposta da função
    - Assim é possível tratar a resposta de forma diferente, dependendo de se ocorreu algum erro em meio ao processo ou se tudo ocorreu como deveria

### O lado ruim das callback:

- Nem tudo são flores, o principal problema em se usar esse método é que seu resultado estará unicamente naquela callback, o que gera `a necessidade de executar uma coisa dentro da outra`

### Exemplo:

> ## Callback hell
>

```
const fs = require('fs');
fs.readFile('file1.txt', (err, file1Content) => {
  if (err) return console.log(Erro ao ler arquivo 1: ${err.message});
  console.log(Lido file1.txt com ${file1Content.byteLength} bytes);
  fs.readFile('file2.txt', (err, file2Content) => {
    if (err) return console.log(Erro ao ler o arquivo 2: ${err.message});
    console.log(Lido file2.txt com ${file2Content.byteLength} bytes);
    fs.readFile('file3.txt', (err, file3Content) => {
      if (err) return console.log(Erro ao ler o arquivo 3: ${err.message});
      console.log(Lido file3.txt com ${file3Content.byteLength} bytes);
    });
  });
});
```
Como mostrado no código acima, a legibilidade se torna muito mais complexa devido a necessidade de criar uma callback dentro de outra callback até concluir sua tarefa.

### Possível solução:

```
const fs = require('fs');
const file3Callback = (err, file3Content) => {
  if (err) return console.log(Erro ao ler o arquivo 3: ${err.message});
  console.log(Lido file3.txt com ${file3Content.byteLength} bytes);
};
const file2Callback = (err, file2Content) => {
  if (err) return console.log(Erro ao ler o arquivo 2: ${err.message});
  console.log(Lido file2.txt com ${file2Content.byteLength} bytes);
  fs.readFile('file3.txt', file3Callback);
};
const file1Callback = (err, file1Content) => {
  if (err) return console.log(Erro ao ler arquivo 1: ${err.message});
  console.log(Lido file1.txt com ${file1Content.byteLength} bytes);
  fs.readFile('file2.txt', file2Callback);
};
fs.readFile('file1.txt', file1Callback);
```
Como tentativa de tornar o código mais legível, são criadas varias funções com o único intuito de chamar a próxima callback, apesar de torná-lo um pouco mais legível, ainda não é performático.

---
## Promises

> A cereja do bolo para as callbacks, melhorando sua legibilidade e deixando seu código muito mais intuitivo.
>

- Sua principal diferença é que ao invés de uma única callback receber tanto o sucesso quanto o erro, ela tera duas callback com funções únicas, uma lidará com o erro e a outra com o sucesso. 

### Ok, mas como essa mágica acontece?

Observe os dois casos a seguir:

```
function dividirNumerosSemPromises(num1, num2) {
  if (num2 == 0) throw new Error("Não pode ser feito uma divisão por zero");

  return num1 / num2;
}

try {
  const resultado = dividirNumeros(2, 1);
  console.log(`resultado: ${resultado}`);
} catch (e) {
  console.log(e.message);
}
```
Na função síncrona `dividirNumerosSemPromises` o resultado da callback já é tratado dentro da função.

```
function dividirNumerosComPromises(num1, num2) {
  const promise = new Promise((resolve, reject) => {
    if (num2 == 0) reject(new Error("Não pode ser feito uma divisão por zero"));
    const resultado = num1 / num2;
    resolve(resultado)
  });
  return promise;
}
dividirNumeros(2, 1)
  .then(result => console.log(sucesso: ${result}))
  .catch(err => console.log(erro: ${err.message}));
```
Agora na função assíncrona `dividirNumerosComPromises` o resultado não é tratado dentro da função, mas sim onde ela está sendo chamada. Assim, com uma mesma função é possível tratar a resposta em inúmeras formas diferentes

### Then e resolve x Catch e reject

Caso não tenha percebido, no segundo caso essas duas palavrinhas são utilizadas, mas o que elas significam?

- Then: forma de tratar o sucesso de uma callback, pode ser utilizado varias vezes no mesmo contexto;
- Catch: igualmente ao then, porém sua tarefa é tratar o erro.

### Estrutura da Promese:

```
const p = new Promise((resolve, reject) => {
  // Aqui é onde vamos realizar a lógica que precisamos
  // para "tentar cumprir" a promessa
});
```
Ao escrevê-la, não se esqueça de utilizar a palavra-chave `new` e da arrow function como parâmetro da `Promese`.

```
const fs = require('fs');
function readFilePromise (fileName) {
  return new Promise((resolve, reject) => {

    fs.readFile(fileName, (err, content) => {
      if (err) return reject(err);
      resolve(content);
    });

  });
}
```
Perceba, no exemplo acima estamos utilizando do modulo interno `fs` apenas com o intuito de ilustrar, o importante aqui é perceber como o `resolve` e o `reject` são utilizados.

```
readFilePromise('./file.txt') // A função me promete que vai ler o arquivo
  .then((content) => { // Caso ela cumpra o que prometeu
    console.log(Lido arquivo com ${content.byteLength} bytes); // Escrevo o resultado no console
  })
  .catch((err) => { // Caso ela não cumpra o que prometeu
    console.error(Erro ao ler arquivo: ${err.message}); // Escrevo o erro no console
  });
```

Sei que é muita coisa, mas aos pouquinhos você vai pegar o jeito :)