async/await: Promises com sintaxe síncrona.
A sintaxe moderna para consumir Promises. async function, await, try/catch/finally. Paralelo vs. sequencial e o erro clássico de await dentro de loop.
A lição anterior apresentou Promises com .then() e .catch(). Funciona, mas o encadeamento pode ficar verboso quando há muitos passos. async/await é uma sintaxe alternativa que transforma código assíncrono em algo que parece síncrono — mais fácil de ler, mais fácil de depurar.
A sintaxe async/await
async antes de uma função transforma ela em uma função assíncrona. Funções assíncronas sempre retornam uma Promise — mesmo que o return seja um valor primitivo, ele é empacotado em uma Promise cumprida.
await pausa a execução da função assíncrona até a Promise à direita se estabelecer, e então retorna o valor. Fora de uma função async, await não funciona — exceto no nível mais alto de um módulo ES (top-level await, disponível com type="module").
// com .then() — encadeamento
function buscarArtigosThen() {
return fetch("/artigos.json")
.then(response => {
if (!response.ok) throw new Error(`Erro ${response.status}`);
return response.json();
})
.then(artigos => artigos.filter(a => a.publicado))
.catch(erro => {
console.error(erro);
return [];
});
}
// com async/await — parece síncrono
async function buscarArtigos() {
const response = await fetch("/artigos.json");
if (!response.ok) throw new Error(`Erro ${response.status}`);
const artigos = await response.json();
return artigos.filter(a => a.publicado);
} Dois awaits no fetch: o primeiro aguarda a resposta HTTP (os headers chegarem). O segundo aguarda o body ser lido e parseado como JSON. Essa é a estrutura padrão de qualquer chamada fetch.
Tratamento de erros com try/catch
Com async/await, erros em Promises rejeitadas se comportam como exceções síncronas — capturáveis com try/catch:
async function carregarArtigos() {
mostrarSpinner();
try {
const response = await fetch("/artigos.json");
if (!response.ok) {
throw new Error(`Servidor retornou ${response.status}`);
}
const artigos = await response.json();
exibirArtigos(artigos);
} catch (erro) {
// captura erros de rede E erros lançados manualmente
console.error("Erro ao carregar artigos:", erro.message);
exibirMensagemErro("Não foi possível carregar os artigos. Tente novamente.");
} finally {
// sempre executa — esconde o spinner independente do resultado
esconderSpinner();
}
} finally é o equivalente de .finally() nas Promises — código de limpeza que deve sempre rodar: esconder spinners, liberar locks, fechar conexões. Sem finally, você precisaria repetir esconderSpinner() no bloco try (sucesso) e no bloco catch (erro).
Paralelo vs. sequencial
Este é o erro mais comum com async/await. Quando você escreve await a(); await b(), as operações são sequenciais: b só começa depois que a termina. Se elas são independentes, você está desperdiçando tempo:
// ❌ sequencial — total: tempo de A + tempo de B
async function carregarDadosSequencial() {
const tags = await buscarTags(); // espera terminar
const autor = await buscarAutor(); // só começa depois
const config = await buscarConfig(); // só começa depois
return { tags, autor, config };
// se cada um leva 300ms: total = 900ms
}
// ✅ paralelo — total: tempo do mais lento
async function carregarDadosParalelo() {
const [tags, autor, config] = await Promise.all([
buscarTags(),
buscarAutor(),
buscarConfig(),
]);
return { tags, autor, config };
// se cada um leva 300ms: total = ~300ms
} O critério é simples: as operações são independentes (o resultado de uma não afeta o input da outra)? Use Promise.all. São dependentes (você precisa do resultado de A para chamar B)? Use await sequencial.
O erro clássico: await dentro de for
Um erro frequente é usar await dentro de um for...of para processar uma lista — sem perceber que o loop fica sequencial:
// ❌ sequencial — cada artigo espera o anterior
async function processarArtigosSequencial(artigos) {
for (const artigo of artigos) {
await salvarCurtida(artigo.id); // um por vez — 100 artigos = 100× o tempo de uma operação
}
}
// ✅ paralelo — todos começam ao mesmo tempo
async function processarArtigosParalelo(artigos) {
await Promise.all(artigos.map(artigo => salvarCurtida(artigo.id)));
// todos os salvamentos começam em paralelo
} Se você precisa que os itens sejam processados na ordem e em paralelo (com concorrência limitada), existem padrões específicos para isso — mas para a maioria dos casos do blog, Promise.all(array.map(...)) é a resposta.
Funções async no contexto do blog
Com async/await, o script.js do blog fica muito mais legível. As funções que interagem com a rede têm async, e o código que as chama usa await:
async function carregarArtigos() {
const lista = document.getElementById("lista-artigos");
try {
lista.innerHTML = '<p class="carregando">Carregando artigos…</p>';
const response = await fetch("/artigos.json");
if (!response.ok) throw new Error(`Erro ${response.status}`);
const artigos = await response.json();
const publicados = artigos.filter(a => a.publicado);
lista.innerHTML = publicados.map(a => `
<article class="card-artigo" data-id="${a.id}">
<h2>${a.titulo}</h2>
<p>Por ${a.autor}</p>
<button class="botao-curtir" aria-pressed="false">🤍 Curtir</button>
</article>
`).join("");
} catch (erro) {
lista.innerHTML = `<p class="erro">Falha ao carregar: ${erro.message}</p>`;
}
}
async function enviarFormulario(dados) {
const botaoEnviar = document.getElementById("botao-enviar");
botaoEnviar.disabled = true;
botaoEnviar.textContent = "Enviando…";
try {
const response = await fetch("/contato", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(dados),
});
if (!response.ok) throw new Error(`Erro ${response.status}`);
exibirNotificacao("sucesso", "Mensagem enviada com sucesso!");
} catch (erro) {
exibirNotificacao("erro", "Falha ao enviar. Tente novamente.");
} finally {
botaoEnviar.disabled = false;
botaoEnviar.textContent = "Enviar mensagem";
}
}
// inicializar ao carregar a página
carregarArtigos(); Resumo
async functionsempre retorna uma Promise.await expressãopausa a execução e retorna o valor da Promise quando ela se resolve.awaitdentro detry/catchtrata rejeições de Promise como exceções síncronas — o padrão de tratamento de erros é consistente com o resto do JavaScript.finallyexecuta sempre — use para limpeza (esconder spinners, reabilitar botões).- Sequencial (
await a(); await b()): operações rodam uma após a outra. Use quando dependem umas das outras. - Paralelo (
await Promise.all([a(), b()])): operações iniciam simultaneamente. Use quando são independentes. - O erro clássico:
awaitdentro defor...ofcria loop sequencial. UsePromise.all(array.map(...))para processar em paralelo.
O que uma função async sempre retorna?
O que acontece quando await é usado em uma Promise que rejeita?
Qual é o problema de usar await dentro de um loop for...of com múltiplas operações independentes?
Qual é a vantagem de async/await sobre .then().catch()?
Aula concluída
Quase lá.