Implementar o tratamento de erros ao usar a API Fetch

Este artigo demonstra algumas abordagens de tratamento de erros ao trabalhar com a API Fetch. A API Fetch permite fazer uma solicitação a um recurso de rede remoto. Quando você faz uma chamada de rede remota, sua página da Web fica sujeita a vários possíveis erros de rede.

As seções a seguir descrevem possíveis erros e como escrever código que ofereça um nível razoável de funcionalidade resistente a erros e condições de rede inesperadas. O código resiliente mantém os usuários satisfeitos e um nível padrão de serviço para seu site.

Antecipe possíveis erros de rede

Esta seção descreve um cenário em que o usuário cria um novo vídeo chamado "My Travels.mp4" e tenta fazer o upload dele em um site de compartilhamento de vídeos.

Ao trabalhar com o Fetch, é fácil considerar o caminho ideal em que o usuário faz o upload do vídeo. No entanto, há outros caminhos que não são tão simples, mas que os desenvolvedores da Web precisam planejar. Esses caminhos (infelizes) podem acontecer devido a um erro do usuário, condições ambientais inesperadas ou um bug no site de compartilhamento de vídeos.

Exemplos de erros do usuário

  • O usuário faz o upload de um arquivo de imagem (como JPEG) em vez de um arquivo de vídeo.
  • O usuário começa a enviar o arquivo de vídeo errado. Em seguida, durante o upload, o usuário especifica o arquivo de vídeo correto para upload.
  • O usuário clica acidentalmente em "Cancelar envio" enquanto o vídeo está sendo enviado.

Exemplos de mudanças ambientais

  • A conexão de Internet fica off-line enquanto o vídeo está sendo enviado.
  • O navegador é reiniciado enquanto o vídeo é enviado.
  • Os servidores do site de compartilhamento de vídeos são reiniciados enquanto o vídeo está sendo enviado.

Exemplos de erros no site de compartilhamento de vídeos

  • O site de compartilhamento de vídeos não consegue processar um nome de arquivo com espaço. Em vez de "My Travels.mp4", ele espera um nome como "My_Travels.mp4" ou "MyTravels.mp4".
  • O site de compartilhamento de vídeos não pode fazer upload de um vídeo que exceda o tamanho máximo de arquivo aceitável.
  • O site de compartilhamento de vídeos não aceita o codec do vídeo enviado.

Esses exemplos podem e acontecem no mundo real. Talvez você já tenha encontrado exemplos como esse no passado. Vamos escolher um exemplo de cada uma das categorias anteriores e discutir os seguintes pontos:

  • Qual é o comportamento padrão se o serviço de compartilhamento de vídeo não conseguir processar o exemplo fornecido?
  • O que o usuário espera que aconteça no exemplo?
  • Como podemos melhorar o processo?
Ação O usuário começa a enviar o arquivo de vídeo errado. Em seguida, durante o upload, o usuário especifica o arquivo de vídeo correto para upload.
O que acontece por padrão O arquivo original continua sendo enviado em segundo plano enquanto o novo arquivo é enviado ao mesmo tempo.
O que o usuário espera O usuário espera que o upload original seja interrompido para que não haja desperdício de largura de banda extra da Internet.
O que pode ser melhorado O JavaScript cancela a solicitação de busca do arquivo original antes que o novo arquivo comece a ser enviado.
Ação O usuário perde a conexão de Internet durante o envio do vídeo.
O que acontece por padrão A barra de progresso do upload parece estar presa em 50%. Eventualmente, a API Fetch vai ter um tempo limite e os dados enviados serão descartados. Quando a conectividade da Internet retornar, o usuário terá que fazer o upload do arquivo novamente.
O que o usuário espera O usuário espera receber uma notificação quando não for possível fazer o upload do arquivo e que o upload seja retomado automaticamente em 50% quando ele estiver de volta on-line.
O que pode ser melhorado A página de envio informa o usuário sobre problemas de conectividade e garante que o envio será retomado quando a conectividade for restabelecida.
Ação O site de compartilhamento de vídeos não consegue processar um nome de arquivo com espaço. Em vez de "My Travels.mp4", ele espera nomes como "My_Travels.mp4" ou "MyTravels.mp4".
O que acontece por padrão O usuário precisa esperar o upload ser concluído. Quando o arquivo é enviado e a barra de progresso mostra "100%", ela exibe a mensagem: "Tente novamente".
O que o usuário espera O usuário espera receber informações sobre as limitações de nome de arquivo antes do início do upload ou pelo menos no primeiro segundo do upload.
O que pode ser melhorado O ideal é que o serviço de compartilhamento de vídeos ofereça suporte a nomes de arquivos com espaços. As opções alternativas são notificar o usuário sobre as limitações de nome de arquivo antes do início do upload. Ou o serviço de compartilhamento de vídeos pode rejeitar o envio com uma mensagem de erro detalhada.

Processar erros com a API Fetch

Os exemplos de código a seguir usam await de nível superior (suporte a navegador) porque esse recurso pode simplificar seu código.

Quando a API Fetch gera erros

Este exemplo usa uma instrução bloco try/catch para capturar erros gerados no bloco try. Por exemplo, se a API Fetch não conseguir buscar o recurso especificado, um erro será gerado. Em um bloco catch como esse, ofereça uma experiência significativa ao usuário. Se uma interface comum que representa algum tipo de progresso, como um ícone de carregamento, for mostrada ao usuário, você poderá realizar as seguintes ações em um bloco catch:

  1. Remova o ícone de carregamento da página.
  2. Forneça mensagens úteis que expliquem o que deu errado e quais opções o usuário pode usar.
  3. Com base nas opções disponíveis, mostre um botão "Tentar de novo" ao usuário.
  4. Em segundo plano, envie os detalhes do erro para o serviço de rastreamento de erros ou para o back-end. Essa ação registra o erro para que ele possa ser diagnosticado mais tarde.
try {
  const response = await fetch('https://website');
} catch (error) {
  // TypeError: Failed to fetch
  console.log('There was an error', error);
}

Em uma fase posterior, enquanto você diagnostica o erro registrado, é possível escrever um caso de teste para detectar esse erro antes que os usuários percebam que algo está errado. Dependendo do erro, o teste pode ser de unidade, integração ou aceitação.

Quando o código de status da rede representa um erro

Este exemplo de código faz uma solicitação para um serviço de teste HTTP que sempre responde com o código de status HTTP 429 Too Many Requests. Curiosamente, a resposta não chega ao bloco catch. Um status 404, entre outros códigos de status, não retorna um erro de rede, mas é resolvido normalmente.

Para verificar se o código de status HTTP foi bem-sucedido, use uma das seguintes opções:

  • Use a propriedade Response.ok para determinar se o código de status estava no intervalo de 200 a 299.
  • Use a propriedade Response.status para determinar se a resposta foi bem-sucedida.
  • Use outros metadados, como Response.headers, para avaliar se a resposta foi bem-sucedida.
let response;

try {
  response = await fetch('https://httpbin.org/status/429');
} catch (error) {
  console.log('There was an error', error);
}

// Uses the 'optional chaining' operator
if (response?.ok) {
  console.log('Use the response here!');
} else {
  console.log(`HTTP Response Code: ${response?.status}`)
}

A prática recomendada é trabalhar com pessoas da sua organização e equipe para entender os possíveis códigos de status de resposta HTTP. Os desenvolvedores de back-end, as operações de desenvolvimento e os engenheiros de serviço às vezes podem fornecer insights exclusivos sobre possíveis casos extremos que você não antecipou.

Quando há um erro ao analisar a resposta da rede

Este exemplo de código demonstra outro tipo de erro que pode surgir com a análise de um corpo de resposta. A interface Response oferece métodos convenientes para analisar diferentes tipos de dados, como texto ou JSON. No código abaixo, uma solicitação de rede é feita para um serviço de teste HTTP que retorna uma string HTML como o corpo da resposta. No entanto, uma tentativa é feita para analisar o corpo da resposta como JSON, gerando um erro.

let json;

try {
  const response = await fetch('https://httpbin.org/html');
  json = await response.json();
} catch (error) {
  if (error instanceof SyntaxError) {
    // Unexpected token < in JSON
    console.log('There was a SyntaxError', error);
  } else {
    console.log('There was an error', error);
  }
}

if (json) {
  console.log('Use the JSON here!', json);
}

Você precisa preparar seu código para receber vários formatos de resposta e verificar se uma resposta inesperada não quebra a página da Web para o usuário.

Considere o seguinte cenário: você tem um recurso remoto que retorna uma resposta JSON válida e que é analisado com sucesso pelo método Response.json(). Pode acontecer de o serviço ficar indisponível. Quando o tempo acabar, uma 500 Internal Server Error será retornada. Se as técnicas de tratamento de erros adequadas não forem usadas durante a análise de JSON, isso poderá causar a quebra da página para o usuário porque um erro não tratado será gerado.

Quando a solicitação de rede precisa ser cancelada antes da conclusão

Este exemplo de código usa um AbortController para cancelar uma solicitação em andamento. Uma solicitação em andamento é uma solicitação de rede que foi iniciada, mas não foi concluída.

Os cenários em que você pode precisar cancelar uma solicitação em andamento podem variar, mas dependem do seu caso de uso e do ambiente. O código abaixo demonstra como transmitir um AbortSignal para a API Fetch. O AbortSignal é anexado a um AbortController, e o AbortController inclui um método abort(), que indica ao navegador que a solicitação de rede precisa ser cancelada.

const controller = new AbortController();
const signal = controller.signal;

// Cancel the fetch request in 500ms
setTimeout(() => controller.abort(), 500);

try {
  const url = 'https://httpbin.org/delay/1';
  const response = await fetch(url, { signal });
  console.log(response);
} catch (error) {
  // DOMException: The user aborted a request.
  console.log('Error: ', error)
}

Conclusão

Um aspecto importante do processamento de erros é definir as várias partes que podem dar errado. Para cada cenário, verifique se você tem um substituto adequado para o usuário. Em relação a uma solicitação de busca, faça perguntas como:

  • O que acontece se o servidor de destino cair?
  • O que acontece se o Fetch receber uma resposta inesperada?
  • O que acontece se a conexão de Internet do usuário falhar?

Dependendo da complexidade da sua página da Web, você também pode esboçar um fluxograma que descreva a funcionalidade e a interface do usuário para diferentes cenários.