Toda vez que um site carrega dados de uma API, lê um arquivo ou espera uma resposta do banco de dados, está lidando com código assíncrono. Para quem está aprendendo, esse conceito é um dos mais confusos do JavaScript — e também um dos mais importantes. Sem entender async/await, você não consegue consumir APIs, construir backends com Node.js ou fazer aplicações web reais. A boa notícia: a sintaxe moderna tornou isso muito mais simples do que parecia antes.
Por que JavaScript precisa de assincronia?
JavaScript é single-threaded: executa uma coisa por vez em uma fila. Se o código parasse e esperasse cada requisição à internet (que pode levar 200ms, 500ms, segundos), a página inteira travaria nesse tempo. A solução é assincronia: em vez de esperar, o JavaScript delega a operação lenta, continua executando o resto do código, e retoma quando o resultado chega.
Antigamente, isso era feito com callbacks (passar função a ser chamada quando o resultado chegasse). Funcionava, mas callbacks aninhados criavam o infame “callback hell” — código com muitos níveis de indentação, difícil de ler e de tratar erros. Promises foram a solução seguinte, mais legível. Async/await é a camada mais recente: syntax sugar sobre Promises que faz código assíncrono parecer síncrono.
O que é uma Promise?
Uma Promise é um objeto que representa uma operação assíncrona que pode estar: pendente (ainda em execução), resolvida (concluiu com sucesso) ou rejeitada (falhou com erro). Você cria uma Promise e “promete” que em algum momento terá um resultado ou um erro. fetch("https://api.exemplo.com/dados") retorna uma Promise. Para lidar com o resultado, você usa .then(resultado => faça algo) e .catch(erro => trate o erro).
Encadear Promises é mais legível que callbacks: fetch(url).then(r => r.json()).then(dados => processar(dados)).catch(erro => console.error(erro)). Cada .then recebe o resultado do anterior. .catch ao final trata qualquer erro em toda a cadeia. Isso ainda parece bastante diferente do código síncrono que você está acostumado.
Async/await: a sintaxe que mudou tudo
A palavra-chave async antes de uma função indica que ela retorna uma Promise. Dentro de uma função async, você pode usar await antes de uma Promise para “pausar” a execução da função (sem travar o thread) e esperar o resultado. O código lê de cima para baixo como código síncrono, mas funciona de forma assíncrona.
Buscar dados de uma API com async/await: declare a função com async, use await fetch(url) para esperar a requisição, depois await resposta.json() para converter para objeto JavaScript. O await só pode ser usado dentro de funções async — tentar usá-lo fora resulta em erro de sintaxe. Toda função async retorna uma Promise, mesmo que você retorne um valor comum — ele fica encapsulado numa Promise resolvida.
Tratamento de erros com try/catch
Com async/await, o tratamento de erros usa try/catch — o mesmo mecanismo do código síncrono. Qualquer await que resulte em uma Promise rejeitada lança um exceção que é capturada pelo catch. Isso é muito mais natural do que o .catch() das Promises encadeadas. O bloco finally executa sempre, independente de sucesso ou erro — útil para esconder loading spinners, fechar conexões, etc.
Sempre trate errros em chamadas a APIs externas: a conexão pode falhar, o servidor pode retornar erro 500, o JSON pode ser inválido. Código sem tratamento de erro é bomba-relógio em produção. Uma dica: verifique se a resposta foi bem-sucedida com resposta.ok antes de tentar converter para JSON — mesmo um fetch bem-sucedido tecnicamente pode retornar um status 404 ou 500.
Executando múltiplas operações em paralelo
Usar await sequencialmente em várias operações executa uma por vez, desperdiçando tempo. Promise.all() executa múltiplas Promises em paralelo e espera todas terminarem: const [usuarios, produtos] = await Promise.all([fetchUsuarios(), fetchProdutos()]);. O resultado é um array com os resultados na mesma ordem. Se qualquer Promise falhar, Promise.all rejeita imediatamente.
Promise.allSettled() aguarda todas terminarem independente de sucesso ou falha, retornando o status de cada uma — útil quando você quer os resultados que deram certo mesmo que algum tenha falhado. Promise.race() retorna o resultado da primeira que terminar (útil para implementar timeouts). Com async/await e essas ferramentas, você tem todo o arsenal para escrevar código assíncrono robusto, legível e eficiente — a fundação para consumir qualquer API e construir aplicações web modernas.
Tem um projeto em mente?
Somos especialistas em transformar ideias em produtos digitais. Apps, sites, automações e IA — vamos construir juntos.