Обработка ошибок HTTP запросов

Основы работы с HTTP в Riot.js

Riot.js не предоставляет собственного встроенного механизма для выполнения HTTP-запросов, поэтому чаще всего используется fetch API или сторонние библиотеки, такие как Axios. Управление ошибками в HTTP-запросах требует внимательного подхода, так как сетевые ошибки, ошибки сервера и неправильные данные могут нарушить работу компонентов.

Обработка ошибок при использовании fetch

fetch возвращает промис, который резолвится даже при HTTP-статусах 4xx и 5xx, поэтому простая конструкция then не гарантирует корректную обработку ошибок. Стандартная практика включает проверку свойства response.ok:

fetch('https://api.example.com/data')
  .then(response => {
    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }
    return response.json();
  })
  .then(data => {
    // Работа с полученными данными
  })
  .catch(error => {
    console.error('Ошибка запроса:', error);
  });

Ключевой момент: необходимо выбрасывать ошибку вручную при некорректном статусе ответа, иначе catch не сработает.

Асинхронная обработка ошибок с async/await

Современный подход использует async/await для более читаемого кода:

async function fetchData() {
  try {
    const response = await fetch('https://api.example.com/data');
    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }
    const data = await response.json();
    return data;
  } catch (error) {
    console.error('Ошибка запроса:', error);
    return null;
  }
}

В Riot.js вызов этой функции можно интегрировать в метод компонента или событие:

this.on('mount', async () => {
  this.items = await fetchData();
  this.update();
});

Использование Axios для упрощения обработки ошибок

Axios автоматически бросает исключение при статусах, отличных от 2xx, что упрощает обработку:

import axios from 'axios';

axios.get('https://api.example.com/data')
  .then(response => {
    this.items = response.data;
    this.update();
  })
  .catch(error => {
    if (error.response) {
      // Сервер вернул статус вне диапазона 2xx
      console.error('Ошибка сервера:', error.response.status, error.response.data);
    } else if (error.request) {
      // Запрос был сделан, но ответа не получено
      console.error('Нет ответа от сервера:', error.request);
    } else {
      // Ошибка в настройке запроса
      console.error('Ошибка запроса:', error.message);
    }
  });

Выделение ключевых случаев ошибок:

  1. Ошибка сервера (4xx/5xx) — данные от сервера пришли, но статус говорит об ошибке.
  2. Сетевые ошибки — проблемы с соединением или таймаут.
  3. Ошибки конфигурации запроса — неверный URL, неправильные заголовки, некорректные параметры.

Интеграция обработки ошибок в компонент Riot.js

Для поддержания чистоты кода и повторного использования логики обработки ошибок рекомендуется создавать утилиты:

export async function safeFetch(url) {
  try {
    const response = await fetch(url);
    if (!response.ok) throw new Error(`HTTP ${response.status}`);
    return await response.json();
  } catch (error) {
    console.error('Ошибка fetch:', error);
    return null;
  }
}

В компоненте:

import { safeFetch } from './utils.js';

this.on('mount', async () => {
  this.data = await safeFetch('https://api.example.com/data');
  if (!this.data) {
    this.errorMessage = 'Не удалось загрузить данные';
  }
  this.update();
});

Таким образом, компонент становится устойчивым к сбоям и отображает пользовательский интерфейс даже при проблемах с сетью.

Реактивное отображение ошибок в шаблоне

Riot.js позволяет использовать реактивные переменные для показа ошибок:

<div>
  <ul>
    <li each={item in data}>{item.name}</li>
  </ul>
  <p if={errorMessage} class="error">{errorMessage}</p>
</div>

В скрипте компонента:

this.data = [];
this.errorMessage = '';

this.on('mount', async () => {
  this.data = await safeFetch('https://api.example.com/data');
  if (!this.data) this.errorMessage = 'Ошибка при загрузке данных';
});

Логирование и повторные попытки

Для критичных операций можно внедрять повторные попытки с экспоненциальной задержкой:

async function fetchWithRetry(url, retries = 3, delay = 1000) {
  for (let i = 0; i < retries; i++) {
    try {
      const response = await fetch(url);
      if (!response.ok) throw new Error(`HTTP ${response.status}`);
      return await response.json();
    } catch (error) {
      console.warn(`Попытка ${i + 1} не удалась:`, error);
      if (i < retries - 1) await new Promise(res => setTimeout(res, delay * (i + 1)));
      else throw error;
    }
  }
}

В Riot.js такая функция позволяет компоненту самостоятельно восстанавливаться после временных сбоев сети, сохраняя плавность работы интерфейса.

Рекомендации по организации

  • Разделение логики запроса и компонента. HTTP-запросы лучше вынести в отдельные модули, чтобы компоненты оставались максимально чистыми.
  • Реактивное состояние ошибок. Использование переменных для отображения статусов (loading, error, success) упрощает управление интерфейсом.
  • Логирование для разработки и мониторинга. Всегда фиксировать ошибки в консоли или отправлять их на сервер аналитики.
  • Повторные попытки и таймауты. Для повышения надежности сетевых операций рекомендуется использовать ограничение времени и экспоненциальные задержки.

Эта структура обеспечивает надежную обработку ошибок HTTP-запросов в Riot.js, поддерживает реактивность компонентов и улучшает пользовательский опыт даже при нестабильной сети.