Aprenda / JavaScript do zero

IntermediárioAula 15 de 1616 min0% da trilha

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 resultado

Use 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.

Quiz rápido

Qual a diferença entre Promise.all e Promise.allSettled?
Por que fetch não rejeita a Promise quando a API responde com status 404?