async e await em JavaScript: como usar na prática
async devolve Promise, await só vive dentro dela, e trocar série por paralelo corta o tempo em três — com medições reais e o erro do forEach que não espera.
async declara que uma função sempre devolverá uma Promise; await suspende a
continuação daquela função até a Promise observada terminar. Juntos, eles deixam
o fluxo assíncrono legível em sequência: uma linha abaixo da outra, resultado em
variável e falha tratada com try/catch.
Pense no recibo de uma lavanderia. Você entrega a roupa e recebe um comprovante
de algo que será concluído depois — a Promise. await é esperar aquele pedido
ficar pronto antes de seguir para a próxima etapa que depende dele. A loja
inteira não para enquanto você espera; no JavaScript, o event loop também pode
continuar processando outras tarefas. Quem fica suspensa é somente a execução da
função async naquele ponto.
Duas regras conduzem o restante da lição: uma função async sempre devolve
uma Promise, mesmo quando retorna um número, e await só organiza o fluxo
onde foi escrito. Operações independentes ainda precisam de Promise.all para
começar juntas.
async transforma o retorno em Promise
Repare que o return devolve um objeto comum, e mesmo assim o que sai é uma
Promise:
async function totalDoPedido(id) {
return { id, total: 479.7 };
}
const resultado = totalDoPedido(8241);
console.log(resultado);
console.log(resultado instanceof Promise);
resultado.then((pedido) => console.log('resolvida com', pedido));Consequência prática: quem chama uma função async precisa de await ou de
then. Esquecer é o erro mais frequente da lição, e ele aparece no fim deste
texto com o stack trace real.
Do lado do erro, o espelho é o mesmo: um throw dentro de uma função async
não estoura na hora — ele vira uma Promise rejeitada, exatamente como o reject
de Promise em JavaScript.
Onde o await pode morar
Dentro de função async, ou direto no corpo de um módulo ES. Fora disso, é erro
de sintaxe — o arquivo nem chega a executar. Em CommonJS:
const buscarPedido = (id) => Promise.resolve({ id, total: 479.7 });
function mostrarPedido(id) {
const pedido = await buscarPedido(id);
console.log(pedido.total);
}
mostrarPedido(8241);A mensagem é literal e diz as duas saídas possíveis: marque a função com
async, ou mova o await para o topo de um módulo. O mesmo arquivo salvo como
.mjs dá uma mensagem diferente e mais críptica — SyntaxError: Unexpected reserved word — porque em módulo o await no topo é válido e o parser quebra
só quando ele aparece dentro de uma função comum.
No topo de um módulo ES, funciona sem embrulho nenhum:
const buscarPedido = (id) =>
new Promise((resolve) => setTimeout(() => resolve({ id, cliente: 'Ana', total: 479.7 }), 200));
const pedido = await buscarPedido(8241);
console.log(`${pedido.cliente}: R$ ${pedido.total.toFixed(2)}`);Série contra paralelo: o mesmo código, três vezes mais lento
Três consultas independentes de 300ms cada, simuladas com um setTimeout para
o tempo ficar previsível:
const espera = (ms, valor) => new Promise((resolve) => setTimeout(() => resolve(valor), ms));
const buscarCliente = () => espera(300, { nome: 'Ana' });
const buscarEstoque = () => espera(300, { disponivel: true });
const buscarFrete = () => espera(300, { valor: 24.9 });Escritas uma embaixo da outra, com await em cada linha:
const inicio = Date.now();
const cliente = await buscarCliente();
const estoque = await buscarEstoque();
const frete = await buscarFrete();
console.log(cliente.nome, estoque.disponivel, frete.valor);
console.log('em série:', Date.now() - inicio, 'ms');905ms para três coisas que não dependem uma da outra. Cada await só solta a
próxima linha quando a anterior termina. Disparando as três antes de esperar:
const inicio = Date.now();
const [cliente, estoque, frete] = await Promise.all([
buscarCliente(),
buscarEstoque(),
buscarFrete(),
]);
console.log(cliente.nome, estoque.disponivel, frete.valor);
console.log('em paralelo:', Date.now() - inicio, 'ms');De 905ms para 303ms, sem cache, sem otimizar consulta, sem tocar no servidor: só
mudando quando as chamadas começam. A regra é curta: await em sequência
serve para dependência; se a segunda chamada não usa o resultado da primeira,
ela não deveria estar esperando.
try/catch volta a funcionar
Com await, uma Promise rejeitada lança um erro de verdade — e o try/catch de
sempre pega:
const cobrar = (valor) =>
new Promise((_, reject) =>
setTimeout(() => reject(new Error('cartão recusado pela operadora')), 200),
);
async function finalizarPedido(id, valor) {
try {
const recibo = await cobrar(valor);
console.log('pago:', recibo);
return recibo;
} catch (e) {
console.log('falhou:', e.message);
return null;
} finally {
console.log(`pedido ${id}: fim da tentativa`);
}
}
const recibo = await finalizarPedido(8241, 479.7);
console.log('recibo =', recibo);Note o return null dentro do catch: a função tratou a falha e devolveu um
valor normal, então a Promise dela fica resolvida, não rejeitada. Se você
quer que quem chamou também saiba do problema, o catch precisa relançar com
throw.
await dentro de forEach não espera nada
Este é o bug que mais chega em revisão de código. Parece certo, roda sem erro e faz a coisa errada:
const baixarEstoque = (item) => espera(200, `${item} baixado`);
const itens = ['Teclado mecânico', 'Mouse sem fio', 'Cadeira gamer'];
console.log('inicio');
itens.forEach(async (item) => {
const r = await baixarEstoque(item);
console.log(r);
});
console.log('todos os itens processados?');A mensagem final saiu antes das baixas de estoque. O motivo é mecânico: o
forEach chama a função, recebe uma Promise de volta e joga fora — ele não tem
como esperar o que não olha. Se a linha seguinte fosse “fechar o pedido”, o
pedido fecharia com o estoque ainda intacto.
Com for...of, o await pausa a função inteira a cada volta:
console.log('inicio');
for (const item of itens) {
console.log(await baixarEstoque(item));
}
console.log('todos os itens processados');E quando a ordem não importa, map + Promise.all faz tudo junto:
const inicio = Date.now();
const resultados = await Promise.all(itens.map(baixarEstoque));
console.log(resultados);
console.log('tudo em', Date.now() - inicio, 'ms');600ms em série contra 203ms em paralelo, com o array de resultados na mesma ordem do array de entrada. Os detalhes desse comportamento estão em Promise.all, allSettled e race.
O erro comum: o await esquecido
Você marcou a função como async, envolveu em try/catch e ainda assim o erro
escapa:
async function buscarPedido(id) {
if (id !== 8241) throw new Error(`pedido ${id} não existe`);
return { id, total: 479.7 };
}
function mostrar(id) {
try {
const pedido = buscarPedido(id); // faltou o await
console.log('total:', pedido.total);
} catch (e) {
console.log('o catch nunca roda:', e.message);
}
}
mostrar(9999);
console.log('fim do mostrar');Três sintomas de uma causa só. pedido era a Promise, então pedido.total deu
undefined. O catch não rodou, porque no instante em que o try terminou
ainda não havia erro nenhum. E o processo morreu depois de fim do mostrar, com
a rejeição não tratada.
Escolhendo entre as três formas
| situação | forma | por quê |
|---|---|---|
| o passo 2 usa o resultado do passo 1 | await em sequência |
não há o que paralelizar |
| chamadas independentes, precisa de todas | Promise.all |
tempo do mais lento, não da soma |
| lista grande contra API com limite | for...of com await |
controla o ritmo das requisições |
| precisa de todos os resultados, mesmo com falha | Promise.allSettled |
uma falha não derruba as outras |
Uma última recomendação de estilo: prefira async/await no código que você
escreve e deixe then para casos pontuais de composição. Misturar os dois na
mesma função — um await aqui, um .then() ali — é a receita mais confiável
para perder o fio do que espera o quê.
Meça você mesmo: crie uma função esperar(ms) que resolve após um setTimeout.
Rode três esperas de 100 ms primeiro em sequência e depois com Promise.all,
medindo com performance.now(). A primeira deve ficar perto de 300 ms; a
segunda, perto de 100 ms. Se os tempos forem iguais, confira se você criou as
três Promises antes de esperá-las em conjunto.
O próximo passo da trilha de JavaScript é a rede de verdade: como consumir uma API com fetch, onde todo esse ferramental encontra HTTP, status code e CORS.
Perguntas frequentes
async/await substitui Promise?
Posso usar await sem async no arquivo?
Quando devo usar for...of em vez de Promise.all?
Por que o try/catch não pegou o erro da minha função async?
Dúvidas e comentários
Travou em algum passo? Pergunte aqui — a equipe e outros alunos respondem.
Entrar para perguntarÉ o mesmo login gratuito dos cursos.
Nenhuma dúvida por aqui ainda — a primeira pode ser a sua.
Todo o código deste artigo foi executado em Node 24.16.0, e as saídas exibidas são as reais — como produzimos este conteúdo.
Fontes consultadas
- MDN — async function — developer.mozilla.org
- MDN — await — developer.mozilla.org


