Async/await no JavaScript: o que é e como usar
12 minutos de leitura

Async Await: o que é e para que serve no JavaScript?
O async await no JavaScript é um dos principais recursos para lidar com operações assíncronas, principalmente quando precisamos trabalhar com dados externos, como informações vindas de uma API REST, microserviços ou outros processamentos.
Quando esse tipo de situação acontece, o sistema tem que esperar a resposta antes de seguir usando o resultado, e é aí que funções assíncronas entram em ação.
Chamamos de programação assíncrona a execução de tarefas em "segundo plano", sem que seja necessário gerenciar diretamente threads ou processos.
Em JavaScript, por ser uma linguagem de execução single-thread (ou seja, que executa uma coisa por vez), o código assíncrono separa as operações em dois grupos: ações que ocorrem imediatamente e ações que vão rodar futuramente, após algum evento ou resposta externa.
Trabalhando com front-end, vemos que uma boa parte do que ocorre no âmbito do navegador é event-driven.
Ou seja, o código aguarda algum evento acontecer (por exemplo, o usuário clicar em um botão) antes de executar qualquer código. Outros exemplos de eventos incluem toque na tela, pressionamento de teclas ou movimento do cursor sobre elementos na interface.
Além dessas interações do usuário, há diversas situações no desenvolvimento que podem ser síncronas ou assíncronas.
Para exemplificar, podemos pensar em comunicação. Uma ligação telefônica é um exemplo de comunicação síncrona: quando falamos ao telefone, as informações chegam e saem em sequência, uma após a outra; fazemos uma pergunta, recebemos logo em seguida a resposta, com os dados dessa resposta fazemos outro comentário, etc.
Por outro lado, uma conversa online via algum mensageiro, como o WhatsApp ou ou Telegram, é um exemplo de comunicação assíncrona: enviamos uma mensagem e não ficamos olhando para a tela, esperando, até a outra pessoa responder (ou pelo menos não deveríamos!).
Afinal de contas, não temos como saber quando, e se, essa resposta vai chegar. Mandamos a mensagem e vamos fazer outras coisas enquanto a resposta não chega, ao contrário do telefone.
Com o código, o processo é parecido: um código síncrono é aquele de ocorre em sequência, uma instrução após a outra.
function soma(num1, num2) {
return num1 + num2;
}
console.log(soma(2, 2)) // 4Até aí, tudo normal. O JavaScript executou uma linha após a outra.
Mas o que acontece quando, por exemplo, nosso código precisa receber alguns dados de uma API?
Ao mesmo tempo que é preciso aguardar a requisição e resposta da API, não podemos bloquear o funcionamento de todo o nosso programa; seria a mesma coisa que enviar uma mensagem pelo WhatsApp e ficar esperando a resposta sem fazer mais nada nesse meio tempo.
É nesse tipo de situação, em que precisamos lidar com operações assíncronas, que entram as Promises e o uso de funções como async e await no JavaScript.
Promises (ou promessas) funcionam como uma forma do JavaScript lidar com códigos que dependem do resultado de operações assíncronas, e async await acaba facilitando muito esse fluxo.
Quando enviamos uma requisição de dados a uma API, temos uma promessa de que estes dados irão chegar, mas enquanto isso não acontece, o sistema deve continuar rodando.
Se, por exemplo, o servidor estiver caído, essa promessa de dados pode não se cumprir, e temos que lidar com isso. As Promises trabalham neste contexto - elas são a ferramenta que o JavaScript utiliza para lidar com código assíncrono.
Existem algumas formas de se trabalhar com processamento assíncrono (ou seja, Promises) em JavaScript: utilizando o método `.then()`, as palavras-chave `async` e `await` ou o objeto `Promise` e seus métodos. Aqui, vamos focar no uso de `.then()`, `async/await` e no uso do método `Promise.all`.
Como funcionam Promises e .then() no JavaScript assíncrono?
Já que estávamos falando sobre APIs REST, vamos ver um exemplo usando a Fetch API do JavaScript para buscar dados e convertê-los para o formato JSON.
Esta API (que funciona nativamente nos navegadores atuais e foi integrada ao Node.js na versão 18 em modo experimental, tornando-se estável na versão 21) tem alguns métodos internos e já retorna por padrão uma Promise que vai resolver a requisição, tendo ou não sucesso.
function getUser(userId) {
const userData = fetch(`https://api.com/api/user/${userId}`)
.then(response => response.json())
.then(data => console.log(data.name))
}
getUser(1); // "Nome Sobrenome"Destrinchando o código acima: a função `getUser()` recebe um id de usuário como parâmetro, para que seja passado para o endpoint REST fictício. A função `fetch()` recebe como parâmetro o endpoint e retorna uma Promise.
E como funcionam as Promises?
Promises têm um método chamado `.then()`, que recebe uma função callback e retorna um "objeto-promessa".
Não é um retorno dos dados, é a promessa do retorno destes dados.
Assim, podemos escrever o código do que irá acontecer em seguida, com os dados recebidos pela Promise, e o JavaScript vai aguardar a resolução da Promise sem pausar o fluxo do programa.
O resultado pode ou não estar pronto ainda, e não há forma de pegar o valor de uma Promise de modo síncrono; Só é possível requisitar à Promise que execute uma função quando o resultado estiver disponível - seja ele o que foi solicitado (os dados da API, por exemplo), ou uma mensagem de erro caso algo tenha dado errado com a requisição (o servidor pode estar fora do ar, por exemplo).
No exemplo acima: ao iniciarmos uma cadeia de promessas - no caso, para fazer uma requisição HTTP - enquanto a resposta está pendente ela retorna um `Promise object`. O objeto, por sua vez, define uma instância do método `.then()`. Ao invés de passar o retorno da função callback diretamente para a função inicial, ela é passada para `.then()`.
Quando o resultado da requisição HTTP chega, o corpo da requisição é convertido para JSON e este valor convertido é passado para o próximo método `.then()`.
A cadeia de funções `fetch().then().then()` não significa que há múltiplas funções callbacks sendo usadas com o mesmo objeto de resposta, e sim que cada instância de `.then()` retorna, por sua vez, um `new Promise()`. Toda a cadeia é lida de forma síncrona na primeira execução, e em seguida executada de forma assíncrona.
Capturando erros com Promises
O manejo de erros é importante em operações assíncronas. Quando o código é síncrono, ele pode lançar pelo menos uma exceção mesmo sem nenhum tipo de tratamento de erros. Porém, no assíncrono, exceções não tratadas muitas vezes passam sem aviso e fica muito mais difícil de debugar.
Não há como utilizar o `try/catch` quando usamos o .then(), pois a computação só será efetuada após o retorno do objeto-Promise. Então devemos passar funções que executem as alternativas, para o caso de sucesso ou falha da operação. Por exemplo:
function getUser(userId) {
const userData = fetch(`https://api.com/api/user/${userId}`)
.then(response => response.json())
.then(data => console.log(data.name))
.catch(error => console.log(error))
.finally(() => /*{ aviso de fim de carregamento }*/)
}
getUser(1); // "Nome Sobrenome"Além do método `.then()` que recebe o objeto-Promise para ser resolvido, o método `.catch()` retorna no caso de rejeição da Promise. Além disso, o último método, `.finally()`, é chamado independente de sucesso ou falha da promessa e a função callback deste método é sempre executada por último.
Esta função pode ser usada, por exemplo, para fechar uma conexão ou dar algum aviso de fim de carregamento.
Resolvendo várias promessas
No caso de várias promessas que devem ser resolvidas pelo programa (por exemplo, alguns dados em endpoints REST diferentes), pode-se utilizar `Promise.all`:
const endpoints = [
"https://api.com/api/user/1",
"https://api.com/api/user/2",
"https://api.com/api/user/3",
"https://api.com/api/user/4"
]
const promises = endpoints.map(url => fetch(url).then(res => res.json()))
Promise.all(promises)
.then(body => console.log(body.name))No exemplo acima, um array de endpoints (`endpoints`) é percorrido com `.map` e as promessas resultantes do `fetch` passadas para a variável `promises` em um novo array. Todo este array de promessas será percorrido e resolvido por `Promise.all` - no exemplo acima a função callback apenas passa para `console.log` a propriedade fictícia `name`.
Uma Promise pode estar em estado "pendente" (pending) ou "resolvida" (settled). Quando está "resolvida", pode ser "concluída" (fulfilled) ou "rejeitada" (rejected). Após a Promise ser concluída ou rejeitada, ela não muda mais de estado.
Uma Promise podem estar "pendente" (pending) ou "resolvida" (settled). Os estados possíveis, uma vez que uma Promise está settled, são "concluída" (fulfilled) ou "rejeitada" (rejected). Após passar de pending para settled e se definir como fulfilled ou rejected, a Promise não muda mais de estado.
Como usar async await no JavaScript (com exemplos práticos)
As palavras-chave `async` e `await`, implementadas a partir do ES2017 (ES8), são uma sintaxe que simplifica a programação assíncrona, facilitando o fluxo de escrita e leitura do código; assim é possível escrever código que funciona de forma assíncrona, porém é lido e estruturado de forma síncrona.
O `async/await` também trabalha com o código assíncrono baseado em Promises, porém esconde as promessas para que a leitura e a escrita seja mais fluídas.
Ao definir uma função como `async`, é possível utilizar a palavra-chave `await` antes de qualquer expressão que retorne uma Promise. Assim, a execução da função será pausada até que a Promise seja resolvida.
A palavra-chave `await` recebe uma Promise e a transforma em um valor de retorno (ou lança uma exceção em caso de erro). Quando utilizamos `await`, o JavaScript vai aguardar até que a Promise finalize.
Se for finalizada com sucesso (o termo utilizado é fulfilled), o valor obtido é retornado. Se a Promise for rejeitada (o termo utilizado é rejected), é retornado o erro lançado pela exceção.
Um exemplo:
let response = await fetch(`https://api.com/api/user/${userId}`); // await JavaScript em ação
let userData = await response.json();Só é possível usar `await` em funções declaradas com a palavra-chave `async`. Ou seja, sempre que quiser criar uma função assíncrona no JavaScript, você deve definir a função como 'async function'. Veja como fazer:
async function getUser(userId) {
let response = await fetch(`https://api.com/api/user/${userId}`);
let userData = await response.json();
return userData.name; // nas linhas de return não é necessário usar await
}Uma função assincrona em JavaScript é criada usando a palavra-chave `async`. Isso faz com que o retorno dessa função seja sempre uma Promise. Se a Promise for resolvida com sucesso, você recebe o valor normalmente e, em caso de erro, pode capturar a exceção usando try/catch.
Essa é a base da estrutura de funções com async await JavaScript.
Para executar a função `getUser()`, já que ela retorna uma Promise, pode-se usar `await`:
exibeDadosUser(await getUser(1)) Lembrando que `await` só funciona dentro de funções `async`. Caso contrário, ainda é possível usar `.then()` normalmente:
getUser(1).then(exibeDadosUser).catch(reject) Resolvendo várias promessas
No caso de várias promessas que devem ser resolvidas para a execução do programa (por exemplo, alguns dados em endpoints REST diferentes), pode-se utilizar `async/await` em conjunto com `Promise.all`:
async function getUser(userId) {
let response = await fetch(`https://api.com/api/user/${userId}`);
let userData = await response.json();
return userData;
}
let [user1, user2] = await Promise.all([getUser(1), getUser(2)])Há diferenças entre .then() e async/await?
Em relação à sintaxe, o método `.then()` é mais comum em abordagens funcionais, enquanto `async/await` costuma ser mais utilizado em métodos de classes e fluxos procedurais.
O `async/await` surgiu como uma opção mais "legível" ao `.then()`, e como vimos acima é possível usar os dois métodos em um mesmo código e, a partir da versão 10 do Node.js, ambas as formas são equivalentes em termos de performance.
O `async/await` simplifica a escrita e a leitura do código assíncrono, mas, por padrão, aguarda a resolução de uma Promise por vez.
Como resolver esse tipo de caso, por exemplo, requisitando uma array de id de pedidos de determinado cliente de um comércio:
async function getCustomerOrders(customerId) {
const response = await fetch(`https://api.com/api/customer/${customerId}`)
const customer = await response.json()
return await Promise.all(customer.orders.map(async (orderId) => {
const response = await fetch(`https://api.com/api/order/${orderId}`)
const orderData = await response.json()
return orderData.amount
}))
}No caso acima, usamos `Promise.all` em conjunto com `.map` para fazer todas as requisições, resolver todas as promessas e processar todos os resultados em um único array.
Iterações com async/await
Mas e se for necessário lidar com várias Promises, mas sem executar todas simultaneamente?
Um exemplo clássico é acessar um banco de dados com milhares de registros. Fazer todas as requisições ao mesmo tempo pode causar problemas de desempenho e até sobrecarregar o serviço.
Nesses casos, o uso de `async/await` em estruturas sequenciais permite resolver uma Promise por vez, evitando sobrecarregar o sistema.
async function printCustomer(customerId){
//lógica fictícia da função
}
async function getAndPrintAllCustomers() {
const sql = 'SELECT id FROM customers'
const customers = await db.query(sql, [])
for (const customer of customers) {
await printCustomer(customer.id)
}
}No caso acima, não queremos fazer todas as requisições ao banco de uma vez, e sim de forma sequencial - em cada iteração do `for`, o `await` vai resolver uma promessa por vez.
Confira um video explicando tudo isso e muito mais
Aqui o DevSoutinho explica pra gente em um video sensacional. É mais um exemplo para você praticar e testar seus conhecimentos:
E um outro vídeo com bastante callbacks:
Resumo: Async Await no JavaScript na prática
Neste artigo aprendemos sobre o que são Promises e como trabalhar com programação assíncrona em JavaScript utilizando `.then()` e também o método mais moderno `async/await`.
Gostou do artigo e quer saber mais? Aqui na Alura temos a Formação em Front End, para você aprender e se desenvolver, além da Formação React
Avalie este artigo
Autor(a)
Juliana Amoasei
Desenvolvedora JavaScript com background multidisciplinar, sempre aprendendo para ensinar e vice-versa. Acredito no potencial do conhecimento como agente de mudança pessoal e social. Atuo como instrutora na Escola de Programação da Alura e, fora da tela preta, me dedico ao Kung Fu e a nerdices em geral.
Inscreva-se em nossa Newsletter
Fique por dentro de conteúdos, insights e oportunidades do universo tech. Receba novidades e lançamentos direto no seu e-mail.



