Библиотека localForage предоставляет унифицированный API поверх
различных механизмов хранения (IndexedDB, WebSQL, localStorage), при
этом сохраняя асинхронную модель работы. Одной из ключевых задач при
работе с хранилищем становится обход всех сохранённых записей. Для этого
используются два принципиальных подхода: метод iterate и
ручной обход через keys с последующим
getItem.
Эти подходы решают схожую задачу, но имеют разную архитектурную модель, производительность и поведенческие особенности.
iterate является специализированным методом библиотеки,
предназначенным для последовательного прохода по всем записям
хранилища.
localforage.iterate((value, key, iterationNumber) => {
// обработка элемента
});
Метод принимает callback, который вызывается для каждой записи:
value — значение, сохранённое по ключуkey — строковый ключ записиiterationNumber — порядковый номер итерацииГлавная особенность заключается в том, что обход выполняется внутри реализации драйвера хранилища, без необходимости вручную получать список ключей или выполнять множественные запросы.
Поведение можно охарактеризовать следующими свойствами:
Альтернативный подход заключается в комбинации двух методов:
const keys = await localforage.keys();
for (const key of keys) {
const value = await localforage.getItem(key);
}
Этот метод состоит из двух этапов:
localforage.keys()
Возвращает массив всех ключей, хранящихся в базе.
localforage.getItem(key)
Каждый ключ требует отдельного асинхронного запроса.
iterate работает как единый поток обхода, где:
Фактически это ближе к cursor-based iteration в IndexedDB.
Ручной подход строится вокруг двухфазной модели:
Это создаёт дополнительный слой абстракции:
iterate:
keys + getItem:
При большом объёме данных разница становится существенной.
iterate:
keys + getItem:
В IndexedDB реализация iterate часто использует курсоры
(IDBCursor), что обеспечивает:
В ручном подходе каждый getItem может запускать
отдельную транзакцию или запрос, что увеличивает накладные расходы.
Контроль ограничен callback-функцией:
localforage.iterate((value, key) => {
if (key === 'stop') return; // не прерывает обход
});
Особенность: стандартный return не останавливает
итерацию. Для прекращения обхода требуется использование исключений:
localforage.iterate((value, key) => {
if (key === 'stop') throw new Error('break');
});
Это делает управление потоком менее интуитивным, но обеспечивает непрерывность курсорной обработки.
Ручной подход позволяет полностью управлять процессом:
for (const key of keys) {
if (key === 'stop') break;
const value = await localforage.getItem(key);
}
Здесь доступны:
breakcontinuePromise.allОбработка строго последовательная. Это обусловлено тем, что метод:
Параллелизм внутри iterate отсутствует.
Позволяет строить разные модели:
for (const key of keys) {
await localforage.getItem(key);
}
const values = await Promise.all(
keys.map(k => localforage.getItem(k))
);
Возможность параллелизма делает этот подход гибким, но потенциально более тяжёлым для памяти и драйвера.
Так как используется курсорная модель:
Так как список ключей фиксируется в момент вызова
keys():
Это делает поведение более предсказуемым.
Использование iterate приводит к архитектуре, где
хранилище рассматривается как поток данных. Это сближает модель с
обработкой стримов.
Использование keys + getItem формирует модель, где
хранилище выступает как индексируемая база данных с возможностью
выборочного доступа и последующей агрегации.
Разница проявляется особенно сильно при масштабировании: