Promises e async/await: pare de se perder no assíncrono
Entenda o que uma Promise realmente representa, a diferença entre await em sequência e Promise.all em paralelo, e os erros mais comuns como esquecer o await ou deixar um erro sem tratamento.
Nos posts sobre event loop, circuit breaker e fila de mensagem, usei async e await o tempo todo como se fossem óbvios. Pra muita gente não são, e o pior é que o código costuma funcionar o bastante pra esconder o mal-entendido, até o dia em que uma requisição fica lenta sem motivo ou um erro some sem deixar rastro.
O que é uma Promise
Uma Promise é um objeto que representa um valor que ainda não existe, mas vai existir (ou vai falhar) no futuro. Ela tem três estados: pendente, resolvida (com um valor) ou rejeitada (com um erro).
const promessa = fetch("https://api.exemplo.com/usuarios");
console.log(promessa); // Promise { <pending> }Quando você chama fetch, a resposta não está pronta ainda. O que você recebe na hora é a Promise, uma espécie de recibo que diz "eu te entrego isso depois". O JavaScript segue executando o resto do código enquanto a rede trabalha, que é exatamente o comportamento do event loop que já expliquei aqui.
then e catch: o jeito antigo
Antes do async/await, o resultado era tratado encadeando funções:
fetch("/api/usuarios")
.then((res) => res.json())
.then((usuarios) => console.log(usuarios))
.catch((erro) => console.error(erro));Funciona, mas com várias operações dependentes o código vira uma escada de .then, difícil de ler e de depurar.
async/await: a mesma coisa, mais legível
async/await é açúcar sintático sobre Promises. Dentro de uma função async, o await pausa a execução daquela função até a Promise resolver, sem travar o resto do programa:
async function buscarUsuarios() {
try {
const res = await fetch("/api/usuarios");
const usuarios = await res.json();
console.log(usuarios);
} catch (erro) {
console.error(erro);
}
}O código lê de cima pra baixo, como se fosse síncrono, e o try/catch funciona como você já conhece. Só vale lembrar que toda função async devolve uma Promise, mesmo que você retorne um valor simples.
O erro de performance mais comum: await em sequência
async function carregarPagina() {
const usuario = await buscarUsuario();
const pedidos = await buscarPedidos();
const produtos = await buscarProdutos();
return { usuario, pedidos, produtos };
}Se as três buscas não dependem uma da outra, esse código as executa uma depois da outra: se cada uma leva 300 ms, o total é 900 ms. A segunda só começa depois que a primeira termina, sem necessidade nenhuma.
async function carregarPagina() {
const [usuario, pedidos, produtos] = await Promise.all([
buscarUsuario(),
buscarPedidos(),
buscarProdutos(),
]);
return { usuario, pedidos, produtos };
}Com Promise.all, as três começam ao mesmo tempo e o total cai pra cerca de 300 ms, o tempo da mais lenta. A regra é simples: se uma operação depende do resultado da outra, use await em sequência. Se são independentes, dispare todas juntas.
Um cuidado: Promise.all rejeita assim que qualquer uma falha, descartando as outras. Quando você quer o resultado de todas, mesmo que algumas deem erro, existe o Promise.allSettled.
Os erros que mais aparecem
Esquecer o await. O código não quebra, ele só trabalha com a Promise em vez do valor:
const usuario = buscarUsuario(); // faltou o await
console.log(usuario.nome); // undefined, porque usuario é uma PromiseUsar await dentro de forEach. O forEach não espera as Promises, então o código seguinte roda antes de o trabalho terminar:
// não espera ninguém
itens.forEach(async (item) => {
await salvar(item);
});
// espera uma por uma
for (const item of itens) {
await salvar(item);
}
// espera todas, em paralelo
await Promise.all(itens.map((item) => salvar(item)));Deixar a rejeição sem tratamento. Uma Promise rejeitada que ninguém captura vira um erro não tratado, que em versões recentes do Node derruba o processo inteiro. Toda chamada assíncrona precisa ter, em algum nível acima, um try/catch ou um .catch, como no post sobre tratamento de erro.
Fechando
Promise é a promessa de um valor futuro, async/await é a forma legível de esperar por ele, e Promise.all é o que separa um código que espera à toa de um que aproveita o tempo. Quase todo bug assíncrono vem de uma dessas três ideias mal encaixadas: esquecer de esperar, esperar em fila o que podia ir junto, ou esperar sem prever que pode falhar.
Direto na sua
caixa de entrada.
Um aviso por e-mail sempre que eu publicar um post novo. Sem spam, sem newsletter chata, só isso.
Leia também
- Regex sem medo: o básico que resolve 90% dos casos6 de novembro de 2026 · 4 min
- Tailwind CSS: vale a pena ou é só moda?5 de novembro de 2026 · 4 min
- Motion graphics com Opus 5.5: o que um prompt não conta4 de novembro de 2026 · 8 min
- HTTP/1.1, HTTP/2 e HTTP/3: o que mudou de verdade30 de outubro de 2026 · 4 min