// js · Web Platform Advent #3
async/await em JavaScript: um guia prático
Como async e await funcionam, como se relacionam com as promises, o tratamento de erros com try/catch, o await em laços e a armadilha serial-vs-paralelo — com exemplos executáveis.
As palavras-chave async e await são sintaxe sobre as promises. Elas permitem escrever código assíncrono que se lê de cima para baixo como código síncrono, mas continuando não bloqueante por baixo dos panos. Se você conhece promises, já conhece async/await — ele apenas remove a cerimônia do .then().
O básico
Marque uma função como async e você poderá usar await dentro dela. await pausa essa função até a promise ser decidida, e então lhe entrega o valor resolvido:
async function getUser(id) {
const res = await fetch('/api/user/' + id);
const user = await res.json();
return user.name;
}
getUser(42).then((name) => console.log(name)); Duas coisas para lembrar: uma função async sempre retorna uma promise (o que você retornar é envolvido em uma), e await só funciona dentro de uma função async — exceto no nível superior de um módulo ES, onde o await de nível superior é permitido.
async/await vs promises
É a mesma maquinaria. Esta cadeia de promises…
fetch('/api/user')
.then((res) => res.json())
.then((user) => console.log(user.name)); …é exatamente equivalente a esta versão async, que a maioria das pessoas acha mais fácil de ler e depurar:
const res = await fetch('/api/user');
const user = await res.json();
console.log(user.name); Você pode misturá-las livremente — faça await de qualquer promise, inclusive do resultado de Promise.all().
Tratamento de erros com try/catch
Com async/await você captura promises rejeitadas usando o try/catch comum, a mesma construção que usa para erros síncronos:
async function loadUser(id) {
try {
const res = await fetch('/api/user/' + id);
if (!res.ok) throw new Error('HTTP ' + res.status);
return await res.json();
} catch (err) {
console.error('Could not load user:', err.message);
return null;
} finally {
console.log('loadUser finished');
}
} Um await rejeitado lança uma exceção naquela linha, então o bloco catch roda. O bloco finally roda quer tenha ocorrido um erro ou não.
A grande armadilha: await serial vs paralelo
O erro mais comum com async/await é aguardar tarefas independentes uma após a outra. Isso roda em série — a segunda requisição não começa até a primeira terminar:
// Slow: total time = a + b + c
const a = await fetchA();
const b = await fetchB();
const c = await fetchC(); Se as tarefas não dependem umas das outras, inicie-as todas primeiro e depois aguarde-as juntas com Promise.all. Agora elas rodam em paralelo:
// Fast: total time ≈ the slowest one
const [a, b, c] = await Promise.all([fetchA(), fetchB(), fetchC()]); await em laços
Um laço for simples com await processa os itens um de cada vez, o que é correto quando cada passo depende do anterior ou você precisa evitar sobrecarregar um servidor:
for (const id of ids) {
const item = await fetchItem(id); // sequential — one after another
console.log(item);
} Para processá-los de forma concorrente, mapeie para um array de promises e faça await Promise.all. Note que forEach não espera por callbacks async — use map + Promise.all em vez disso:
const items = await Promise.all(ids.map((id) => fetchItem(id))); Referência rápida
| Objetivo | Padrão |
|---|---|
| Aguardar uma promise | const x = await p; |
| Tratar erros | try { await ... } catch (e) { ... } |
| Rodar tarefas independentes em paralelo | await Promise.all([...]) |
| Laço sequencial | for...of com await |
| Laço concorrente | map → Promise.all |
async/await não substitui as promises — torna-as legíveis. Mantenha Promise.all na sua caixa de ferramentas para o paralelismo, recorra a try/catch para os erros e fique atento ao await serial acidental, e você terá o JavaScript assíncrono bem sob controle.