Tratando erro em código assíncrono
Promise.all derruba tudo se UMA requisição falhar — às vezes é exatamente isso que você não quer.
Você já sabe usar try/catch com async/await. Só que existe um detalhe sobre fetch que costuma pegar quem está começando de surpresa, e uma escolha entre dois métodos de Promise que muda completamente o comportamento quando algo falha no meio de várias buscas.
fetch não rejeita em erro HTTP — só em falha de rede
async function buscarDados() {
try {
const resposta = await fetch("/api/dados");
if (!resposta.ok) {
throw new Error(`Erro HTTP: ${resposta.status}`);
}
return await resposta.json();
} catch (erro) {
console.error("Falha ao buscar dados:", erro);
throw erro; // repassa pro chamador decidir o que fazer
}
}Uma resposta 404 ou 500 ainda é, do ponto de vista do fetch, uma requisição que "deu certo" — o servidor respondeu alguma coisa. fetch só rejeita a Promise em problema de rede de verdade (sem conexão, DNS que não resolve). Por isso o if (!resposta.ok) manual é necessário — sem ele, um erro 500 passaria batido pelo catch.
Promise.all — uma rejeição derruba todas
try {
const [usuario, pedidos, endereco] = await Promise.all([
buscarUsuario(1),
buscarPedidos(1),
buscarEndereco(1),
]);
} catch (erro) {
// se QUALQUER uma das três falhar, cai aqui — mesmo que as outras duas tenham funcionado
console.error("Alguma busca falhou:", erro);
}Isso é o comportamento certo quando as três informações só fazem sentido juntas — de nada adianta ter o usuário sem os pedidos dele, por exemplo.
Promise.allSettled — cada resultado isolado
const resultados = await Promise.allSettled([
buscarUsuario(1),
buscarPedidos(1),
buscarEndereco(1),
]);
resultados.forEach((r) => {
if (r.status === "fulfilled") {
console.log("Deu certo:", r.value);
} else {
console.error("Essa falhou:", r.reason);
}
});
// nenhuma falha impede as outras de aparecer no resultadoUse Promise.allSettled quando cada busca é independente e você quer mostrar o que deu certo, mesmo que uma delas tenha falhado — por exemplo, um painel com vários cartões, cada um vindo de uma API diferente.
AbortController — cancelando um fetch em andamento
const controlador = new AbortController();
fetch("/api/dados-grandes", { signal: controlador.signal })
.then((res) => res.json())
.catch((erro) => {
if (erro.name === "AbortError") {
console.log("Requisição cancelada");
}
});
// em algum outro momento (ex.: usuário saiu da tela, ou disparou uma busca nova)
controlador.abort();Sem cancelar, uma requisição antiga que demora mais que uma nova pode chegar depois dela e sobrescrever um dado mais atualizado com um mais velho — AbortController evita esse tipo de condição de corrida.
Exercício
Você tem await Promise.all([buscarPerfil(), buscarPreferencias()]), mas quer que, se buscarPreferencias() falhar, a tela ainda mostre o perfil (com preferências padrão). Reescreva usando Promise.allSettled.