Архитектура localForage основана на абстракции хранилищ,
где каждый драйвер реализует единый контракт API. Независимо от того,
используется ли IndexedDB, WebSQL, localStorage или пользовательский
драйвер, библиотека взаимодействует с ним через строго определённый
набор методов. Эти методы формируют минимально необходимый интерфейс,
без которого драйвер не может быть зарегистрирован и использован.
Базовая структура драйвера
Любой драйвер в localForage представляет собой объект,
который реализует набор асинхронных операций. Все методы обязаны
возвращать Promise, даже если внутренняя реализация
синхронная. Это обеспечивает единообразие поведения API и позволяет
localForage работать с разными типами хранилищ через один
слой абстракции.
Минимальный набор обязательных свойств:
_driver — уникальное строковое имя драйвера
_initStorage — инициализация хранилища
setItem
getItem
removeItem
clear
length
key
keys
iterate
_driver
Идентификатор драйвера, используемый системой для регистрации и
выбора хранилища.
Требования:
- Тип:
string
- Должен быть уникальным в рамках окружения
- Используется в
localForage.defineDriver и
setDriver
Пример:
_driver: 'customDriver'
_initStorage
Метод инициализации внутреннего состояния драйвера. Вызывается один
раз при активации драйвера.
Сигнатура:
_initStorage(options): Promise<void>
Поведение:
- Создаёт или открывает соединение с хранилищем
- Инициализирует внутренние структуры (например, таблицы, ключевые
пространства)
- Сохраняет конфигурацию (например, name, storeName, version)
Важные требования:
- Должен завершаться успешно перед использованием других методов
- При ошибке обязан отклонять
Promise
- Не должен выполнять операций чтения/записи данных вне своей области
ответственности
setItem
Основной метод записи данных.
Сигнатура:
setItem(key, value): Promise<value>
Поведение:
- Сохраняет значение по ключу
- Перезаписывает существующее значение
- Возвращает сохранённое значение
Требования:
- Поддержка сериализации значений (если требуется драйвером)
- Корректная обработка типов (строки, объекты, массивы)
- Гарантия атомарности операции, если это поддерживается
платформой
Поведение при конфликте:
- Старое значение полностью заменяется новым
- Частичные обновления не допускаются
getItem
Получение значения по ключу.
Сигнатура:
getItem(key): Promise<value>
Поведение:
- Возвращает значение, сохранённое под указанным ключом
- Если ключ отсутствует — возвращает
null
Требования:
- Должен корректно обрабатывать несуществующие ключи без ошибок
- Обязан возвращать данные в исходном виде (после десериализации, если
применимо)
removeItem
Удаление значения по ключу.
Сигнатура:
removeItem(key): Promise<void>
Поведение:
- Удаляет запись, связанную с ключом
- Если ключ отсутствует — операция считается успешной
Особенности:
- Не должен генерировать ошибку при удалении несуществующего
ключа
- Должен освобождать связанные ресурсы (если применимо к
платформе)
clear
Полная очистка хранилища.
Сигнатура:
clear(): Promise<void>
Поведение:
- Удаляет все записи текущего namespace (storeName)
- Не затрагивает другие базы или namespace, если они существуют
Требования:
- Должен быть максимально быстрым (предпочтительно
batch-операция)
- Обязан гарантировать полное удаление данных
length
Получение количества записей.
Сигнатура:
length(): Promise<number>
Поведение:
- Возвращает число ключей в текущем хранилище
Требования:
- Значение должно быть актуальным на момент вызова
- Не допускается возврат кэшированных устаревших значений без
синхронизации
key
Получение ключа по индексу.
Сигнатура:
key(n): Promise<string>
Поведение:
- Возвращает ключ по порядковому номеру
- Индексация начинается с
0
Особенности реализации:
- Порядок ключей не обязан быть стабильным между вызовами
- Используется для итерационных сценариев
keys
Получение списка всех ключей.
Сигнатура:
keys(): Promise<string[]>
Поведение:
- Возвращает массив всех ключей в текущем хранилище
Требования:
- Результат должен быть полным
- Порядок ключей может быть произвольным, если иное не поддерживается
драйвером
iterate
Метод итерации по всем записям хранилища.
Сигнатура:
iterate(iteratorCallback): Promise<any>
callback:
function iteratorCallback(value, key, iterationNumber)
Поведение:
- Вызывает callback для каждой записи
- Позволяет выполнять агрегирование, поиск, фильтрацию
Требования:
- Должен обходить все элементы без пропусков
- iterationNumber должен увеличиваться последовательно
- Должен поддерживать асинхронные внутренние операции драйвера
Особенности:
- Результат
Promise может возвращать значение, если
callback прерывает итерацию через возврат значения (в зависимости от
реализации)
- Порядок обхода может отличаться от физического хранения
Общие требования к
реализации методов
Асинхронность
Все методы обязаны быть асинхронными и возвращать
Promise, даже если реализация синхронная:
return Promise.resolve(result);
Ошибки
При любой ошибке:
Promise должен быть отклонён (reject)
- Ошибка должна содержать диагностическую информацию
- Не допускается “тихое” подавление ошибок
Контекст хранения
Каждый драйвер обязан учитывать изоляцию:
- различие между database / storeName / namespace
- недопустимость утечки данных между инстансами
Согласованность данных
При последовательных операциях:
setItem → getItem должен возвращать
актуальное значение
removeItem должен гарантировать отсутствие данных после
завершения
Поддержка конкурентного
доступа
Драйвер должен учитывать возможные параллельные операции:
- одновременные
setItem
- одновременные
clear и setItem
- конкурентные
iterate
При невозможности полной синхронизации требуется внутренний механизм
очередей или блокировок.
Минимальный контракт
Драйвер считается валидным только при наличии следующего набора:
_driver
_initStorage
setItem
getItem
removeItem
clear
length
key
keys
iterate
Отсутствие любого из этих методов делает драйвер несовместимым с
системой localForage.