Uso Assíncrono: Entenda a diferença sobre Callbacks, Promises e Async/Await!
Tags: javascript, beginners, programming
Published: 2023-03-20 17:56:36

No desenvolvimento de uma aplicação, seja web ou mobile nem sempre a execução de alguma tarefa será feita no momento que foi requisitada, o que faz ser necessária a existência de assincronicidade na aplicação, pois sem isso sua aplicação seria pausada a cada nova requisição fazendo o usuário aguardar até o final de sua execução para continuar. Neste artigo relembraremos sobre o que seria a assíncronicidade dita, veremos as diferenças sobre as funções assíncronas:Callback, Promises e Async/Await.

**Relembrando o que é um fluxo assíncrono**

No Javascript, e outras linguagens de programação, os códigos são executados de forma sequencial,com o computador lendo o código conforme ele foi escrito, mas como citado anteriormente existem casos que essa execução não será dessa maneira, mas sim de forma assíncrona, ou seja, não esperar a finalização de um fluxo para executar o próximo, isso seria uma função assíncrona.


**As diferenças entre Callback, Promises e Async/Await:**

Algo a se recordar é que embora sejam usadas para continuar acontecimentos exigidos pelo usuário ou sistema, essas 3 opções ainda estão de certa forma ligadas entre si, mas por quê Asyn/Await é a melhor opção atual para uso.


- **CallBack**

Os Callbacks são funções que são passadas como argumentos para outras funções assíncronas. Eles são executados quando a tarefa assíncrona é concluída, permitindo que a aplicação continue a executar outras tarefas enquanto espera pela resposta da tarefa assíncrona.

Vejamos um exemplo claro de uso da CallBack:

```
//fs =módulo fireSistem que lê e gerencia os arquivos 
const fs = require('fs')

console.log(1)
// Em outra situação, a leitura seria ordenada indo de 1 a 3, mas nesse caso estamos fazendo uma requisição de ler o arquivo, que ocupará um tempo inoportuno, veja a seguir:


function callback(error, contents){
console.log(error, String(contents))
}

fs.readFile('./teste1.txt', callback)

console.log(2)
console.log(3)
```

A execução do código acima será:

```
1
2
3
// O resultado abaixo é o conteúdo do arquivo teste1.txt
null 10/11/12
```

No exemplo acima, o código continuará a ser lido de maneira ordenada, mas como foi requisitado um pedido assíncrono, ele vai executá-lo e após o finalizar, ele o retornará. Imagine que em uma loja de salgados, enquanto o cozinheiro está preparando um prato, ele recebe mais um pedido do restaurante. Ele não irá parar para começar do 0 o novo pedido e terminá-lo para depois continuar no anterior, ele irá guardar o novo pedido e continuar assim que o pedido primário estiver pronto, ou o que for menos custoso em questão de tempo para ele. O javaScript irá empilhar em uma estrutura, que ao estiver pronta a leitura do arquivo, ele irá retornar esse trecho de código, e por ser uma chamada de retorno, de volta, ela recebe o nome  _callBack_ .

O grande problema do callBack é que quanto mais requisições de leitura forem feitas, mais o código irá crescer "para frente":

```
fs.readFile('./teste1.txt',(error,contents) => {
     fs.readFile('./teste2.txt',(error2,contents2) => {
       console.log(error, String(contents))
       console.log(error2, String(contents2))
     })
})

............continua...
```

A longo prazo, a manutenção do código ficará quase impossível, pois ele começará a fazer uma curva em S. É nesse caso que entrará o _Promises_ , vejamos.


- **Promises**

As Promises são objetos que representam uma tarefa assíncrona e fornecem uma interface para lidar com o resultado da tarefa quando ela é concluída. As Promises podem estar em três estados: pendente, resolvida ou rejeitada. Quando uma Promise é resolvida, a função .then() é executada. Quando é rejeitada, a função .catch() é executada.

Terminada a explicação rápida sobre Promises, como ele entraria para "soluciona" o problema da callback?

Quando eu chamo um callback eu o "chamo" depois, eu retorno uma promesa que no futuro eu vou trazer um valor para o que se pede. O que seria isso tudo na prática?

```
const readFile = file => new Promise((resolve, reject) => {
     fs.readFile(file, (error,contents) => {
       if(error){
         reject(error)
       }
       else{
        resolve(contents)
       }
     }) 
})

readFile('./teste1.txt')
 .then( contents => {
  console.log(null, String(contents))
 })
```
A Promise pode receber o parâmetro que irá retornar um valor imediatamente e depois retornar 2 valores na Promise de rejeitar ou aceitar, como dito anteriomente. O resultado será:

```
1
2
3
// O resultado abaixo é o conteúdo do arquivo teste1.txt
null 10/11/12
```

Qual a vantagem do Promises? Ele irá ser escrito para baixo e não para o lado, sendo assim sua manutenção é muito mais fácil e erros cometidos podem ser encontrados mais facilmente. No caso da Promises nós retornamos um valor a ele, e depois poderemos resolver ou rejeitar a Promises. Mas onde entra o Asyn/Await nessa situação? Vejamos:


- **Async/Await**

Por fim, o Async/Await é uma forma mais recente de lidar com tarefas assíncronas. É uma forma mais simplificada de lidar com Promises, permitindo que o código seja escrito de forma mais síncrona, como se a tarefa assíncrona fosse executada imediatamente. O await é usado para esperar que a Promise seja resolvida antes de continuar a execução do código.

Resumidamente o Asyn/Await é um açúcar sintático, ou seja é uma forma melhor de enxergar uma Promise.

```
const init = async() => {

}

console.log(init())

////////////////////

O resultado será o retorno de uma Promises:

1
Promises {undefined}
2
3
```

Pode estar pensando: Já vi como utilizar a Promise, se Async/Await é uma Promise, qual sua vantagem? Com ela, podemos retornar qualquer função que retorne uma Promise, retornando um Await.

```
const init = async() => {
const content = await readFile('./teste1.txt')
console.log(content)

}

console.log(init())

//////////////// Seu resultado:

1
Promises {<pending>}
2
3
```

Para poder ter um resultado completo, sem uma pendência, fazemos assim:

```
const init = async() => {
const content = await readFile('./teste1.txt')
const content2 = await readFile('./teste2.txt')
console.log(String(content))
console.log(String(content2))

}
console.log(init())

////////// O resultado será:
1
2
3
10
11
12
13
20
21
22
```
Com base no código acima, qual seria mais fácil de se fazer manutenção? O Promise, CallBack ou Asyn/Await? Claramente o Async/Await além de ter uma estrutura menor, ele executa da mesmao forma pois ele faz um proxy de usar o Promise que usa o CallBack, sendo assim uma execução bem mais simples e uma escolha excelente. Mas percebeu que nesse caso, está isento de erros, como se faria o código caso tivéssemos que inserir algum?

```
const init = async() => {
try{

const content = await readFile('./teste1.txt')
const content2 = await readFile('./teste3.txt')
console.log(String(content))
console.log(String(content2))
}catch(error){
 console.log(error) 
}

}
console.log(init())
```

Resolvendo o Erro:

```
////// Como trocamos o teste2.txt por teste2.txt que não existe ele tratará o erro, algo interessante de destacar é que se o erro for na primeira linha de leitura,caso fosse no teste1.tsx, ele não executaria a segunda linha, sendo até mesmo mais fácil de tratar erros. 
```

Baseado nisso, vimos que o CallBack executa a operação e a que mais demorar, será chamada de volta depois. Quando transformamos em Promises, nós apenas modificamos sua estrutura de escrita e retornamos que prometemos que iremos retornar aquela função em breve, continuando a ser assíncrono. E no fim, quando usamos o Async/Await em cima de Promises, e ao usar o Await, fazemos o código ser assíncrono, permitindo que tratemos uma operação mais chata de se ler como no caso da Promise na seção que criamos antes, em algo mais simples de ler, sua manutenção e qualidade no final.