Implementa el manejo de errores cuando se usa la API de recuperación

En este artículo, se muestran algunos enfoques de manejo de errores cuando se trabaja con la API de Fetch. La API de Fetch te permite realizar una solicitud a un recurso de red remoto. Cuando realizas una llamada de red remota, tu página web está sujeta a varios posibles errores de red.

En las siguientes secciones, se describen los posibles errores y cómo escribir código que proporcione un nivel razonable de funcionalidad que sea resistente a errores y condiciones de red inesperadas. Un código resiliente mantiene contentos a sus usuarios y mantiene un nivel estándar de servicio para su sitio web.

Anticipa posibles errores de red

En esta sección, se describe una situación en la que el usuario crea un video nuevo llamado "My Travels.mp4" y, luego, intenta subirlo a un sitio web para compartir videos.

Cuando se trabaja con la recuperación, es fácil considerar la ruta correcta en la que el usuario sube correctamente el video. Sin embargo, existen otras rutas que no son tan fluidas, pero para las que los desarrolladores web deben planificar. Estas rutas (defectuosas) pueden ocurrir debido a errores del usuario, condiciones del entorno inesperadas o a causa de un error en el sitio web para compartir videos.

Ejemplos de errores del usuario

  • El usuario sube un archivo de imagen (como JPEG) en lugar de un archivo de video.
  • El usuario comienza a subir el archivo de video equivocado. Luego, en medio de la carga, el usuario especifica el archivo de video correcto que debe subirse.
  • El usuario hace clic accidentalmente en “Cancelar carga” mientras se sube el video.

Ejemplos de cambios ambientales

  • La conexión a Internet se interrumpe mientras se sube el video.
  • El navegador se reinicia mientras se sube el video.
  • Los servidores del sitio web para compartir videos se reinician mientras se sube el video.

Ejemplos de errores del sitio web para compartir videos

  • El sitio web para compartir videos no puede manejar un nombre de archivo con un espacio. En lugar de "My Travels.mp4", espera un nombre como "My_Travels.mp4" o "MyTravels.mp4".
  • El sitio web para compartir videos no puede subir un video que exceda el tamaño de archivo máximo aceptable.
  • El sitio web para compartir no admite el códec de video en el video subido.

Estos ejemplos pueden ocurrir y de hecho suceden en el mundo real. Es posible que hayas encontrado esos ejemplos en el pasado. Elijamos un ejemplo de cada una de las categorías anteriores y analicemos los siguientes puntos:

  • ¿Cuál es el comportamiento predeterminado si el servicio para compartir videos no puede procesar el ejemplo proporcionado?
  • ¿Qué espera el usuario que suceda en el ejemplo?
  • ¿Cómo podemos mejorar el proceso?
Acción El usuario comienza a subir el archivo de video equivocado. Luego, en medio de la carga, el usuario especifica el archivo de video correcto que debe subirse.
Qué sucede de forma predeterminada El archivo original se seguirá subiendo en segundo plano mientras el archivo nuevo se sube al mismo tiempo.
Qué espera el usuario El usuario espera que se detenga la carga original para que no se desperdicie más ancho de banda de Internet.
Qué se puede mejorar JavaScript cancela la solicitud para recuperar el archivo original antes de que el archivo nuevo comience a subirse.
Acción El usuario pierde la conexión a Internet antes de subir el video.
Qué sucede de forma predeterminada La barra de progreso de la carga parece estar detenida en el 50%. Con el tiempo, se agota el tiempo de espera de la API de Fetch y se descartan los datos subidos. Cuando vuelve a conectarse a Internet, el usuario tiene que volver a subir el archivo.
Qué espera el usuario El usuario espera recibir una notificación cuando no se pueda subir un archivo y que se reanude automáticamente al 50% cuando vuelva a estar en línea.
Qué se puede mejorar La página de carga informa al usuario sobre los problemas de conectividad a Internet y le asegura que se reanudará la carga cuando esta se reanude.
Acción El sitio web para compartir videos no puede manejar un nombre de archivo con un espacio. En lugar de "Mis viajes.mp4", espera nombres como "Mis_Viajes.mp4" o "MisViajes.mp4".
Qué sucede de forma predeterminada El usuario debe esperar que la carga termine por completo. Una vez que se suba el archivo y la barra de progreso indica "100%", la barra de progreso mostrará el siguiente mensaje: "Vuelve a intentarlo".
Qué espera el usuario El usuario espera que se le informe sobre las limitaciones en los nombres de archivo antes de que comience la carga o, al menos, en el primer segundo de esta.
Qué se puede mejorar Idealmente, el servicio para compartir videos admite nombres de archivo con espacios. Otras opciones son notificar al usuario sobre las limitaciones en los nombres de archivo antes de que comience la carga. O el servicio para compartir videos debería rechazar la carga con un mensaje de error detallado.

Soluciona errores con la API de Fetch

Ten en cuenta que, en los siguientes ejemplos de código, se usa await de nivel superior (compatibilidad con navegadores) porque esta función puede simplificar tu código.

Cuando la API de Fetch arroja errores

En este ejemplo, se usa una sentencia de bloque try/catch para detectar cualquier error arrojado dentro del bloque try. Por ejemplo, si la API de Fetch no puede recuperar el recurso especificado, se mostrará un error. Dentro de un bloque catch como este, asegúrate de proporcionar una experiencia del usuario significativa. Si se muestra al usuario un ícono giratorio, una interfaz de usuario común que representa algún tipo de progreso, puedes realizar las siguientes acciones en un bloque catch:

  1. Quita el ícono giratorio de la página.
  2. Proporciona mensajes útiles que expliquen qué salió mal y qué opciones puede usar el usuario.
  3. Según las opciones disponibles, muestra el botón "Reintentar" al usuario.
  4. En segundo plano, envía los detalles del error al servicio de seguimiento de errores o al backend. Esta acción registra el error para que pueda diagnosticarse en una etapa posterior.
try {
  const response = await fetch('https://website');
} catch (error) {
  // TypeError: Failed to fetch
  console.log('There was an error', error);
}

Más adelante, durante el diagnóstico del error que registraste, puedes escribir un caso de prueba para detectar ese error antes de que los usuarios se den cuenta de que algo anda mal. En función del error, la prueba puede ser una unidad, una integración o una prueba de aceptación.

Cuando el código de estado de la red representa un error

En este ejemplo de código, se realiza una solicitud a un servicio de prueba HTTP que siempre responde con el código de estado HTTP 429 Too Many Requests. Resulta interesante que la respuesta no llegue al bloque catch. Un estado 404, entre otros códigos de estado, no muestra un error de red, sino que se resuelve normalmente.

Para verificar que el código de estado HTTP se haya realizado correctamente, puedes usar cualquiera de las siguientes opciones:

  • Usa la propiedad Response.ok para determinar si el código de estado estaba en el rango de 200 a 299.
  • Usa la propiedad Response.status para determinar si la respuesta fue exitosa.
  • Usa cualquier otro metadato, como Response.headers, para evaluar si la respuesta fue correcta.
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}`)
}

Te recomendamos trabajar con los miembros de tu organización y tu equipo para comprender los posibles códigos de estado de respuesta HTTP. A veces, los desarrolladores de backend, los ingenieros de servicio y las operaciones de desarrolladores pueden proporcionar estadísticas únicas sobre posibles casos extremos que tal vez no preveas.

Cuando se produce un error al analizar la respuesta de la red

Este ejemplo de código muestra otro tipo de error que puede surgir con el análisis del cuerpo de una respuesta. La interfaz Response ofrece métodos convenientes para analizar diferentes tipos de datos, como texto o JSON. En el siguiente código, se realiza una solicitud de red a un servicio de prueba HTTP que muestra una cadena HTML como cuerpo de la respuesta. Sin embargo, se intenta analizar el cuerpo de la respuesta como JSON, lo que arroja un error.

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);
}

Debes preparar tu código para admitir una variedad de formatos de respuesta y verificar que una respuesta inesperada no dañe la página web del usuario.

Considera la siguiente situación: tienes un recurso remoto que muestra una respuesta JSON válida y se analiza correctamente con el método Response.json(). Puede ocurrir que el servicio falle. Cuando se desactiva, se muestra un 500 Internal Server Error. Si no se usan las técnicas adecuadas de manejo de errores durante el análisis de JSON, esto podría interrumpir la página para el usuario porque se arroja un error no controlado.

Cuándo se debe cancelar la solicitud de red antes de que se complete

En este ejemplo de código, se usa un AbortController para cancelar una solicitud en curso. Una solicitud en tránsito es una solicitud de red que se inició, pero no se completó.

Las situaciones en las que quizás debas cancelar una solicitud en curso pueden variar, pero, en última instancia, depende de tu caso de uso y entorno. En el siguiente código, se muestra cómo pasar un AbortSignal a la API de Fetch. El AbortSignal está vinculado a un AbortController, y el AbortController incluye un método abort(), que le indica al navegador que se debe cancelar la solicitud de red.

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)
}

Conclusión

Un aspecto importante del manejo de los errores es definir las distintas partes que pueden salir mal. En cada situación, asegúrate de tener un resguardo adecuado para el usuario. Con respecto a una solicitud de recuperación, hazte preguntas como las siguientes:

  • ¿Qué sucede si el servidor de destino deja de funcionar?
  • ¿Qué sucede si Fetch recibe una respuesta inesperada?
  • ¿Qué sucede si falla la conexión a Internet del usuario?

Según la complejidad de tu página web, también puedes esbozar un diagrama de flujo que describa la funcionalidad y la interfaz de usuario para diferentes situaciones.