Перехват ошибок в хуках

В библиотеке Navigo хуки предоставляют мощный механизм для управления жизненным циклом маршрутов и обработки событий перед или после их активации. Одной из критически важных задач при работе с хуками является корректная обработка ошибок, возникающих внутри этих функций, чтобы предотвратить неконтролируемое поведение приложения и обеспечить стабильную навигацию.


Основные типы хуков и точки перехвата ошибок

В Navigo используются несколько типов хуков:

  1. before – вызывается перед активацией маршрута. Используется для проверки условий, авторизации или асинхронной подготовки данных.
  2. after – вызывается после успешного завершения маршрута. Полезен для логирования или очистки ресурсов.
  3. error – специальный хук для перехвата ошибок, возникающих как в before, так и в after.

Ошибки, возникшие в хуках before или after, могут быть как синхронными, так и асинхронными (например, ошибки промисов). Обработка таких ошибок требует корректного использования конструкций try/catch и Promise.catch.


Обработка ошибок в хуке before

Хук before принимает два аргумента:

  • done – функция обратного вызова для продолжения маршрута;
  • params – объект с параметрами маршрута.

Простейший пример синхронной обработки ошибок:

const router = new Navigo('/');

router.on('/dashboard', {
  before: (done, params) => {
    try {
      if (!user.isAuthenticated()) {
        throw new Error('Пользователь не авторизован');
      }
      done(); // продолжение маршрута
    } catch (error) {
      console.error('Ошибка в before-хуке:', error);
      router.navigate('/login'); // перенаправление при ошибке
    }
  },
  as: 'dashboard'
});

Ключевые моменты:

  • Все операции, которые могут выбросить исключение, должны быть обернуты в try/catch.
  • Ошибка в хуке before может остановить переход на маршрут, поэтому необходимо либо логировать её, либо перенаправлять пользователя.

Асинхронные ошибки в хуках

Если хук before выполняет асинхронные операции (например, загрузку данных через fetch), ошибки нужно обрабатывать через промисы:

router.on('/profile', {
  before: (done, params) => {
    fetch(`/api/user/${params.id}`)
      .then(response => {
        if (!response.ok) throw new Error('Не удалось загрузить данные пользователя');
        return response.json();
      })
      .then(data => {
        window.userData = data;
        done();
      })
      .catch(error => {
        console.error('Ошибка в асинхронном before-хуке:', error);
        router.navigate('/error');
      });
  },
  as: 'profile'
});

Особенности асинхронной обработки:

  • Вызов done() необходимо выполнять только после успешного завершения всех асинхронных операций.
  • Любая ошибка промиса должна перехватываться через .catch(), иначе она приведёт к необработанному исключению.

Хук after и его особенности

Хук after вызывается после успешного рендеринга маршрута и не влияет на блокировку перехода. Однако ошибки в этом хуке могут нарушить дальнейшую логику приложения:

router.on('/settings', {
  after: (params) => {
    try {
      analytics.trackPageView(params);
    } catch (error) {
      console.error('Ошибка в after-хуке:', error);
    }
  },
  as: 'settings'
});
  • Так как переход уже произошёл, критично только корректно логировать ошибки.
  • Если в after используются сторонние библиотеки, необходимо предусматривать возможность их сбоев.

Глобальный перехват ошибок маршрутизатора

Navigo позволяет задать глобальный обработчик ошибок через метод notFound и специализированный error хук:

router.onError((error) => {
  console.error('Глобальная ошибка маршрутизатора:', error);
  // Отображение страницы ошибки
  showErrorPage(error);
});

Особенности:

  • onError перехватывает все необработанные исключения в хуках и колбэках маршрутов.
  • Рекомендуется использовать его для логирования и уведомления пользователя.

Рекомендации по безопасной обработке ошибок

  1. Всегда оборачивать потенциально опасный код в try/catch или использовать .catch() для промисов.
  2. Для асинхронных операций обязательно ожидать их завершения перед вызовом done().
  3. Логировать ошибки с достаточным контекстом (params, состояние приложения).
  4. Перенаправлять пользователя на страницу ошибки или авторизации, если хук before не может корректно завершиться.
  5. Использовать глобальный onError как резервный механизм на случай пропущенных исключений.

Пример комплексной обработки

router.on('/orders/:id', {
  before: async (done, params) => {
    try {
      const order = await fetchOrder(params.id);
      if (!order) throw new Error('Заказ не найден');
      window.currentOrder = order;
      done();
    } catch (error) {
      console.error('Ошибка загрузки заказа:', error);
      router.navigate('/orders/error');
    }
  },
  after: (params) => {
    try {
      logVisit(params.id);
    } catch (error) {
      console.error('Ошибка логирования визита:', error);
    }
  },
  as: 'orderDetails'
});

router.onError((error) => {
  console.error('Необработанная ошибка Navigo:', error);
  showErrorPage(error);
});

Этот пример демонстрирует безопасную работу с хуками, включая асинхронные операции, обработку ошибок и глобальное логирование.


Таким образом, правильное использование try/catch, .catch() для промисов и глобальных обработчиков ошибок позволяет полностью контролировать состояние приложения при навигации и предотвращает неожиданное поведение маршрутизатора.