Обработка ошибок инициализации

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


Источники ошибок на этапе инициализации

Поведение localForage зависит от среды выполнения, и именно эта зависимость формирует основные классы ошибок.

1. Недоступность IndexedDB

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

  • работа в старых браузерах;
  • отключённые политики безопасности;
  • режимы приватного просмотра с ограничениями;
  • встроенные webview с урезанным API.

В таких случаях попытка инициализации драйвера приводит к отказу и переходу к следующему варианту (WebSQL или localStorage), если он указан.


2. Ограничения приватного режима браузера

Особенно критичным является Safari (iOS/macOS), где в приватном режиме IndexedDB может:

  • выбрасывать исключения при открытии базы;
  • возвращать успешное создание, но блокировать запись;
  • внезапно инвалидировать соединение.

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


3. Отсутствие доступных драйверов

Если разработчик явно задаёт список драйверов:

localforage.setDriver([
  localforage.INDEXEDDB,
  localforage.WEBSQL
]);

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


4. Нарушение контекста безопасности

IndexedDB и часть современных storage API требуют защищённого контекста:

  • HTTPS;
  • localhost как исключение;
  • некоторые sandboxed iframe запрещают доступ.

При нарушении этих условий драйвер не может быть активирован.


5. Ошибки во время открытия хранилища

Даже при наличии поддержки API возможны ошибки:

  • повреждённая база данных;
  • конфликт версий схемы;
  • превышение внутренних ограничений браузера.

Модель инициализации localForage

Инициализация localForage основана на цепочке Promise:

  • выбор драйвера;
  • попытка его подключения;
  • проверка работоспособности через тестовую операцию;
  • установка активного backend.

Ключевой точкой синхронизации является метод:

localforage.ready()

Он возвращает Promise, который:

  • резолвится при успешной инициализации;
  • реджектится при невозможности выбрать рабочий драйвер.

Базовая обработка ошибок через ready()

Основной способ отлова ошибок инициализации — обработка отклонения Promise:

localforage.ready()
  .then(() => {
    // storage готов к работе
  })
  .catch((err) => {
    console.error('Ошибка инициализации storage:', err);
  });

Такой подход охватывает:

  • отсутствие доступных драйверов;
  • фатальные ошибки открытия базы;
  • внутренние ошибки конфигурации.

Важно, что ready() не следует путать с синхронной инициализацией: к моменту вызова Promise библиотека уже может быть частично настроена, но не гарантирует работоспособность backend.


Инициализация с явным контролем драйверов

При ручном указании драйверов обработка ошибок становится более предсказуемой, но требует явного контроля:

async function initStorage() {
  try {
    await localforage.setDriver([
      localforage.INDEXEDDB,
      localforage.WEBSQL,
      localforage.LOCALSTORAGE
    ]);

    await localforage.ready();
  } catch (err) {
    console.error('Не удалось инициализировать localForage:', err);
  }
}

В этом сценарии ошибки могут возникать:

  • при несовместимости драйвера;
  • при отказе браузера;
  • при внутреннем сбое открытия хранилища.

Стратегия fallback при ошибках инициализации

Практическая обработка ошибок почти всегда включает резервные варианты поведения.

1. Переход на in-memory storage

Если все драйверы недоступны, возможно использование временного хранилища:

class MemoryStore {
  constructor() {
    this.store = new Map();
  }

  getItem(key) {
    return Promise.resolve(this.store.get(key) || null);
  }

  setItem(key, value) {
    this.store.set(key, value);
    return Promise.resolve(value);
  }

  removeItem(key) {
    this.store.delete(key);
    return Promise.resolve();
  }
}

2. Деградация функциональности

При отсутствии persistent storage система может:

  • отключить офлайн-режим;
  • отключить кеширование;
  • перевести данные в session-only режим.

3. Повторная инициализация

В некоторых случаях ошибка носит временный характер:

  • загрузка до полной инициализации браузерных API;
  • кратковременная блокировка IndexedDB;
  • переключение контекста iframe.
async function initWithRetry(retries = 3) {
  for (let i = 0; i < retries; i++) {
    try {
      await localforage.ready();
      return;
    } catch (e) {
      if (i === retries - 1) throw e;
      await new Promise(r => setTimeout(r, 200 * (i + 1)));
    }
  }
}

Обработка ошибок setDriver как часть инициализации

Метод setDriver() часто становится источником ошибок, которые воспринимаются как «инициализация не работает»:

  • неподдерживаемый драйвер;
  • конфликт порядка приоритета;
  • попытка установки после начала операций хранения.

Типичный сценарий неправильного использования:

localforage.setItem('key', 'value'); // ранний вызов
localforage.setDriver([...]);        // поздняя инициализация

Это может приводить к частично неконсистентному состоянию.


Защита от race condition при старте

Инициализация storage должна завершаться до начала любых операций чтения/записи. В противном случае возникает гонка состояний.

Корректный паттерн:

let storageReady;

function getStorage() {
  if (!storageReady) {
    storageReady = localforage.ready();
  }
  return storageReady;
}

async function safeSet(key, value) {
  await getStorage();
  return localforage.setItem(key, value);
}

Такой подход гарантирует:

  • единичную инициализацию;
  • отсутствие параллельных конфликтов;
  • предсказуемое состояние драйвера.

Централизованный обработчик ошибок инициализации

В крупных приложениях обработка ошибок localForage обычно выносится в отдельный слой абстракции:

class StorageService {
  constructor() {
    this.initPromise = null;
  }

  init() {
    if (!this.initPromise) {
      this.initPromise = localforage.ready().catch((err) => {
        this.handleInitError(err);
        throw err;
      });
    }
    return this.initPromise;
  }

  handleInitError(err) {
    console.error('Storage init failed:', err);

    // telemetry hook
    // sendToMonitoring(err);
  }

  async set(key, value) {
    await this.init();
    return localforage.setItem(key, value);
  }
}

Такой слой позволяет:

  • централизовать контроль ошибок;
  • интегрировать мониторинг;
  • реализовать fallback-логику без изменения бизнес-кода.

Логирование и наблюдаемость ошибок

Ошибки инициализации относятся к критическому классу событий и должны фиксироваться отдельно от обычных ошибок хранения.

Рекомендуемые поля логирования:

  • тип драйвера;
  • браузер и версия;
  • режим (private/public);
  • timestamp инициализации;
  • причина отказа (stack trace).

Это особенно важно при анализе:

  • Safari private mode issues;
  • IndexedDB quota failures;
  • корпоративных ограничений браузеров.

Поведение при частичной инициализации

Некоторые сценарии приводят к состоянию, когда localForage:

  • создаёт инстанс;
  • но не может подтвердить работоспособность драйвера.

В таких случаях возможны:

  • успешные вызовы setItem с последующим падением;
  • silent failures в старых окружениях;
  • переключение на fallback без уведомления.

Для предотвращения таких ситуаций используется явная проверка:

await localforage.ready();
const driver = localforage.driver();

Обобщённая модель устойчивой инициализации

Надёжная инициализация localForage в нестабильной среде строится на сочетании трёх принципов:

  • ожидание ready() перед любыми операциями;
  • обработка отказа через централизованный catch;
  • наличие резервного механизма хранения.

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