Когда localForage переходит на localStorage

Библиотека localForage построена как абстракция над различными механизмами хранения данных в браузере. Её ключевая идея заключается в том, чтобы автоматически выбирать наиболее подходящее хранилище и предоставлять единый асинхронный API независимо от реальной реализации.

Внутри экосистемы браузерного хранения данных обычно рассматриваются три основных уровня:

  • IndexedDB как основной современный механизм
  • WebSQL как устаревшая, но всё ещё встречающаяся технология
  • localStorage как максимально простой синхронный fallback

Именно последний вариант — localStorage — играет критическую роль в сценарии деградации возможностей, когда более мощные хранилища недоступны или не могут быть использованы.


Позиция localStorage в иерархии драйверов

localForage использует концепцию драйверов. Каждый драйвер — это адаптер, реализующий единый интерфейс:

  • setItem
  • getItem
  • removeItem
  • clear
  • iterate
  • length

При инициализации библиотека пытается выбрать драйвер по приоритету:

  1. IndexedDBDriver
  2. WebSQLDriver
  3. localStorageWrapper

Таким образом, localStorage находится на последнем месте цепочки и рассматривается исключительно как fallback-механизм.


Условия перехода на localStorage

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

1. Отсутствие IndexedDB

Основная причина активации fallback-режима:

  • старые браузеры без IndexedDB
  • окружения с ограниченными API (например, некоторые WebView)
  • нестабильные или повреждённые реализации IndexedDB

В этом случае localForage пытается подключить WebSQL, и только затем localStorage.


2. Ошибки инициализации IndexedDB

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

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

Если IndexedDBDriver не может открыть базу или создать object store, происходит переход к следующему драйверу.


3. Отключённые настройки приватности

В некоторых режимах браузеры ограничивают доступ к долговременному хранилищу:

  • строгий приватный режим
  • блокировка persistent storage
  • ограниченные контейнеры iframe

Если IndexedDB или WebSQL не могут гарантировать запись, localStorage остаётся единственным синхронным вариантом.


4. Принудительная настройка драйвера

Разработчик может явно указать использование localStorage:

localforage.setDriver(localforage.LOCALSTORAGE)

В этом случае остальные драйверы игнорируются полностью.


Особенности localStorage как fallback

Когда localForage переходит на localStorage, поведение библиотеки меняется на фундаментальном уровне, несмотря на сохранение API.

Синхронность операций

localStorage работает синхронно, в отличие от IndexedDB:

  • операции блокируют основной поток
  • отсутствуют транзакции
  • нет очередей запросов

localForage вынужден оборачивать эти операции в Promise, но фактическая работа остаётся синхронной.


Ограничение объёма

localStorage имеет строгие лимиты, зависящие от браузера:

  • обычно 5–10 MB на домен
  • отсутствие масштабируемости
  • отсутствие динамического расширения

localForage не может обойти эти ограничения, поэтому при переходе на localStorage резко возрастает риск ошибок QuotaExceededError.


Отсутствие структурированных данных

localStorage хранит только строки, поэтому:

  • объекты сериализуются через JSON.stringify
  • массивы теряют типизацию
  • бинарные данные требуют дополнительного кодирования

localForage выполняет сериализацию автоматически, но это добавляет накладные расходы.


Как localForage оборачивает localStorage

localStorage не предоставляет асинхронного API, поэтому localForage создаёт адаптер, который имитирует поведение IndexedDB-драйвера.

Принцип работы адаптера

Каждая операция выглядит следующим образом:

  1. Выполняется синхронный вызов localStorage
  2. Результат сразу возвращается
  3. Promise резолвится в следующем тике event loop

Это создаёт иллюзию асинхронности:

setItem(key, value) {
  return new Promise((resolve) => {
    localStorage.setItem(key, serialize(value))
    resolve(value)
  })
}

Отличия поведения при переходе

При переключении на localStorage наблюдаются важные изменения:

1. Производительность

  • IndexedDB: высокопроизводительные пакетные операции
  • localStorage: блокировка UI при каждой записи

Особенно заметно при массовых операциях iterate и setItem.


2. Отсутствие транзакций

localForage в IndexedDB использует транзакции для целостности данных, но при localStorage:

  • каждая операция независима
  • нет rollback-механизма
  • возможна частичная запись при сбое

3. Упрощённая модель конкурентности

IndexedDB поддерживает конкурентный доступ, localStorage — нет:

  • возможны race conditions между вкладками
  • изменения не синхронизируются атомарно
  • события storage используются как слабый механизм уведомлений

Стратегия выбора драйвера

Внутренний алгоритм localForage можно представить как последовательность проверок:

  1. Проверка IndexedDB
  2. Попытка открытия базы
  3. Проверка работоспособности операций
  4. Переход к WebSQL (если доступен)
  5. Переход к localStorage

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


Сценарии, где localStorage становится основным драйвером

Несмотря на то, что localStorage считается fallback-решением, есть реальные сценарии, где он фактически становится основным:

Устаревшие браузеры

  • старые версии Android WebView
  • legacy браузеры корпоративных систем
  • встроенные браузеры приложений

Ограниченные среды выполнения

  • sandbox iframe без IndexedDB
  • специфические режимы приватности
  • некоторые embedded браузеры

Минимальные приложения

Иногда разработчики сознательно ограничивают стек:

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

Ограничения архитектурного характера

Использование localStorage как fallback накладывает ограничения на всю архитектуру localForage:

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

Эти ограничения не устраняются библиотекой, а лишь изолируются через единый API.


Поведение при переключении драйвера во время выполнения

localForage допускает динамическое переключение драйверов, но переход на localStorage сопровождается рядом нюансов:

  • существующие данные не мигрируют автоматически
  • структура ключей может измениться
  • кэшированные значения становятся недействительными

Поэтому смена драйвера обычно рассматривается как инициализационный процесс, а не runtime-операция.


Влияние localStorage на сериализацию и типы данных

localStorage требует строкового представления, поэтому localForage использует слой сериализации:

  • JSON для объектов
  • base64 или arraybuffer-подобные представления для бинарных данных
  • обёртки для восстановления типов

Однако при переходе на localStorage:

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

Поведение ошибок при использовании localStorage

В отличие от IndexedDB, ошибки localStorage носят более ограниченный характер:

  • QuotaExceededError
  • SecurityError (в приватных режимах)
  • DOMException при недоступности хранилища

localForage не имеет возможности компенсировать эти ошибки, поэтому они пробрасываются через Promise rejection.


Итоговая роль localStorage в архитектуре localForage

localStorage в localForage выполняет строго определённую функцию:

  • резервный механизм хранения
  • совместимость с устаревшими средами
  • минимальный уровень работоспособности API

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