W3docs

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 promisesasync/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.

javascript— editable

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.

javascript— editable

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.

javascript— editable

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.

javascript— editable

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:

javascript— editable

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.

javascript— editable

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

  • forEach ignora callbacks async. array.forEach(async ...) não aguarda as promises. Use um loop for...of ou Promise.all(array.map(...)).
  • Não esqueça o await. Chamar uma função async sem await (ou .then()) retorna uma promise pendente e engole erros silenciosamente. Linters frequentemente sinalizam promises "flutuantes".
  • fetch não rejeita em erros HTTP. Sempre verifique response.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.

Prática

Prática
Qual é a função da palavra-chave 'async' em JavaScript?
Qual é a função da palavra-chave 'async' em JavaScript?
Was this page helpful?