JavaScript async/await
Aprenda JavaScript async/await: funções async, await, tratamento de erros com try/catch, execução sequencial vs paralela com Promise.all e armadilhas comuns.
A sintaxe async/await é a forma moderna e legível de trabalhar com código assíncrono em JavaScript. Ela é construída diretamente sobre promises — async/await não as substitui, mas oferece uma sintaxe mais limpa para consumi-las. Em vez de encadear callbacks .then(), você escreve código que é lido de cima para baixo como código síncrono comum, enquanto o motor cuida das esperas nos bastidores.
Este capítulo aborda o que as funções async retornam, como o await pausa a execução, o tratamento de erros com try...catch, a execução de tarefas de forma sequencial versus paralela, o await de nível superior em módulos e as armadilhas que pegam a maioria dos desenvolvedores de surpresa.
Funções async sempre retornam uma promise
Marcar uma função como async faz duas coisas: permite usar await dentro do corpo da função e garante que ela retorne uma promise. Qualquer valor que você return se torna o valor resolvido dessa promise; se você lançar uma exceção com throw, a promise é rejeitada.
Como o valor de retorno é uma promise, quem chama a função ainda precisa usar await (ou .then()) para ler o valor real. Um erro comum de iniciantes é esperar que greet() retorne 'Hello' diretamente.
await pausa até que a promise seja resolvida
O operador await só pode ser usado dentro de uma função async (ou no nível superior de um módulo — veja abaixo). Ele pausa a função até que a promise à sua direita seja resolvida: em caso de cumprimento, retorna o valor resolvido; em caso de rejeição, lança o motivo da rejeição.
É importante ressaltar que await não bloqueia o programa inteiro. Ele apenas suspende a função async atual; o restante do seu código e o event loop continuam em execução.
Você pode usar await com qualquer valor, não apenas com uma promise. Valores que não são promises são encapsulados e resolvidos imediatamente, então await 5 simplesmente retorna 5.
Tratamento de erros com try...catch
Um dos maiores ganhos do async/await é que você trata erros com o mesmo try...catch que já usa para código síncrono. Uma promise rejeitada se transforma em uma exceção lançada no ponto do await, que o bloco catch pode interceptar.
Se você não capturar uma rejeição, ela se torna uma rejeição de promise não tratada. Para uma análise mais aprofundada dos mecanismos do lado da promise, consulte tratamento de erros com promises.
Um exemplo do mundo real: buscar dados e relatar falhas de forma clara.
Observe a verificação explícita de response.ok: o fetch só rejeita em caso de falha de rede, não em status de erro HTTP como 404 ou 500, portanto você deve inspecionar a resposta por conta própria.
Execução sequencial vs. paralela
É aqui que o await é mais frequentemente mal utilizado. Quando você usa await em operações uma após a outra, elas são executadas de forma sequencial — cada uma aguarda a anterior terminar. Isso só é correto quando uma tarefa posterior depende do resultado de uma anterior.
Sequencial (quando as tarefas dependem umas das outras)
async function pipeline() {
const user = await getUser(1); // step 1
const posts = await getPosts(user.id); // needs user.id, so must wait
return posts;
}Paralelo (quando as tarefas são independentes)
Se as tarefas não dependem umas das outras, aguardá-las em sequência é um desperdício de tempo. Inicie todas ao mesmo tempo e depois aguarde juntas com Promise.all:
Promise.all rejeita assim que qualquer uma de suas promises for rejeitada. Se você quiser aguardar todas as promises independentemente de sucesso ou falha, use Promise.allSettled. Consulte a API de promises para conhecer a família completa de combinadores.
await de nível superior em módulos
Dentro de módulos ES (<script type="module"> ou arquivos .mjs), você pode usar await no nível superior sem precisar envolvê-lo em uma função async:
// data.mjs — an ES module
const response = await fetch('https://jsonplaceholder.typicode.com/todos/1');
const todo = await response.json();
export { todo };Isso é útil para a inicialização de módulos, como carregar configurações antes que as exportações do módulo estejam prontas. Observe que um módulo que usa await de nível superior atrasa a avaliação de qualquer módulo que o importe. O await de nível superior funciona apenas em módulos — usá-lo em um script clássico ou em uma função comum é um erro de sintaxe.
Armadilhas comuns
Não use await dentro de um loop para trabalho independente
Usar await dentro de um loop for força as iterações a serem executadas uma de cada vez. Se as iterações forem independentes, isso é desnecessariamente lento.
Use await dentro de um loop apenas quando cada iteração realmente depende da anterior, ou quando você precisa limitar o número de requisições.
Outras armadilhas
forEachignora callbacks async.array.forEach(async ...)não aguarda as promises. Use um loopfor...ofouPromise.all(array.map(...)).- Não esqueça o
await. Chamar uma função async semawait(ou.then()) retorna uma promise pendente e engole erros silenciosamente. Linters frequentemente sinalizam promises "flutuantes". fetchnão rejeita em erros HTTP. Sempre verifiqueresponse.ok, como mostrado acima.
Conclusão
async/await faz com que o JavaScript assíncrono seja lido como código síncrono, mantendo o event loop livre. Lembre-se dos fundamentos: toda função async retorna uma promise, await pausa apenas a função atual, os erros fluem através de try...catch e você deve executar tarefas independentes em paralelo com Promise.all em vez de aguardá-las uma por uma. Para fortalecer a base por trás dessa sintaxe, revise promises e encadeamento de promises.