Обработка ошибок в потоках

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

Обработка ошибок сетевых запросов

Сетевые ошибки являются одной из самых частых причин сбоев. В Workbox каждая стратегия поддерживает возможность перехвата ошибок через промисы. Например, при использовании NetworkFirst можно определить обработчик ошибок следующим образом:

import { NetworkFirst } from 'workbox-strategies';
import { registerRoute } from 'workbox-routing';

const networkFirstStrategy = new NetworkFirst({
  cacheName: 'dynamic-cache',
  networkTimeoutSeconds: 3,
});

registerRoute(
  ({ request }) => request.destination === 'document',
  async ({ event }) => {
    try {
      const response = await networkFirstStrategy.handle({ event });
      if (!response) throw new Error('Сеть недоступна и кэш пуст');
      return response;
    } catch (error) {
      return caches.match('/offline.html');
    }
  }
);

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

  • Использование try/catch позволяет перехватывать исключения как сетевые, так и ошибки стратегии.
  • Возврат fallback-страницы (offline.html) повышает надежность приложения при отсутствии сети.
  • Параметр networkTimeoutSeconds задаёт максимальное время ожидания сети, после которого стратегия переключается на кэш.

Обработка ошибок при работе с кэшем

Ошибки кэширования часто возникают из-за повреждённых данных, ограничения хранилища или конфликтов версий. Workbox предоставляет API для работы с кэшами через методы caches.open, cache.put и cache.match. Все операции с кэшем следует оборачивать в промисы с обработкой ошибок:

async function cacheResource(request, response) {
  try {
    const cache = await caches.open('dynamic-cache');
    await cache.put(request, response.clone());
  } catch (error) {
    console.error('Ошибка кэширования ресурса:', request.url, error);
  }
}

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

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

Глобальная обработка ошибок стратегий

Workbox позволяет настраивать глобальные обработчики ошибок через события fetch или workbox.routing.setCatchHandler. Это особенно полезно для обработки ошибок разных типов в одном месте:

import { setCatchHandler } from 'workbox-routing';

setCatchHandler(async ({ event }) => {
  if (event.request.destination === 'document') {
    return caches.match('/offline.html');
  } else if (event.request.destination === 'image') {
    return caches.match('/images/fallback.png');
  }
  return Response.error();
});

Принципы работы:

  • Любая ошибка, не перехваченная локально в стратегии, автоматически попадёт в catch handler.
  • Позволяет централизованно управлять fallback-контентом для разных типов ресурсов.
  • Поддерживает асинхронные операции, включая динамическое добавление ресурсов в кэш при ошибках.

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

Потоки (Streams API) используются Workbox для работы с большими ресурсами и потоковой передачей данных. Ошибки в потоках нужно обрабатывать через события error и abort:

self.addEventListener('fetch', event => {
  event.respondWith(
    fetch(event.request).then(response => {
      const { readable, writable } = new TransformStream();
      const reader = response.body.getReader();
      const writer = writable.getWriter();

      function pump() {
        return reader.read().then(({ done, value }) => {
          if (done) {
            writer.close();
            return;
          }
          return writer.write(value).then(pump).catch(err => {
            console.error('Ошибка записи в поток:', err);
            writer.abort(err);
          });
        }).catch(err => {
          console.error('Ошибка чтения из потока:', err);
          writer.abort(err);
        });
      }

      pump();
      return new Response(readable, { headers: response.headers });
    }).catch(err => {
      console.error('Ошибка fetch:', err);
      return caches.match('/offline.html');
    })
  );
});

Ключевые аспекты:

  • Потоки необходимо закрывать через writer.close() или прерывать через writer.abort() при ошибках.
  • Использование reader.read() и рекурсивного pump позволяет безопасно передавать данные с контролем ошибок.
  • Обработка ошибок на уровне fetch гарантирует, что пользователь получит fallback, даже если поток прервётся.

Логирование и мониторинг ошибок

Для комплексного мониторинга можно интегрировать логирование ошибок в сторонние сервисы (Sentry, LogRocket) с деталями запроса и состояния кэша. В Workbox можно использовать WorkboxPlugin для расширенной обработки:

import { CacheableResponsePlugin } from 'workbox-cacheable-response';
import { ExpirationPlugin } from 'workbox-expiration';

new CacheableResponsePlugin({
  statuses: [0, 200],
  headers: { 'X-Cacheable': 'yes' },
});

Использование плагинов позволяет:

  • Ограничивать кэширование только успешных ответов.
  • Предотвращать попадание в кэш повреждённых данных.
  • Легко интегрировать обработку ошибок в цепочку стратегий.

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

  • Всегда использовать try/catch вокруг асинхронных операций и стратегий.
  • Для потоков обязательно обрабатывать события error и abort.
  • Для каждой стратегии кэширования предусматривать fallback-контент.
  • Логировать ошибки с контекстом запроса и состояния кэша.
  • Использовать глобальный catch handler для унифицированной обработки исключений.

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