Получение результата из iterate

Метод iterate в localForage представляет собой механизм последовательного обхода всех ключей и значений хранилища с доступом к каждому элементу по очереди. Базовая сигнатура выглядит следующим образом:

localforage.iterate((value, key, iterationNumber) => {
  // обработка элемента
});

Колбэк получает три аргумента:

  • value — значение, сохранённое по текущему ключу
  • key — строковый идентификатор записи
  • iterationNumber — порядковый индекс итерации (начиная с 1)

В отличие от методов getItem или keys, iterate ориентирован не на возврат результата, а на потоковую обработку всех элементов хранилища.


Почему iterate не возвращает результат напрямую

Ключевая особенность модели выполнения заключается в том, что iterate не формирует возвращаемое значение на уровне метода. Следующий код не создаёт накопленный результат:

const result = localforage.iterate((value) => {
  return value;
});

Переменная result не содержит массив или агрегированную структуру данных, поскольку возвращаемое значение колбэка игнорируется.

Причина такого поведения — потоковая природа обработки. Хранилище может содержать большое количество записей, и создание промежуточной структуры по умолчанию не навязывается API.


Сбор результатов в массив

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

const result = [];

await localforage.iterate((value, key) => {
  result.push({ key, value });
});

console.log(result);

Здесь формируется массив объектов, где каждый элемент содержит ключ и значение. Такая структура используется для последующей обработки, фильтрации или сериализации.

Вариант с трансформацией значений:

const values = [];

await localforage.iterate((value) => {
  values.push(value);
});

Результат формируется строго в порядке итерации, который определяется внутренним драйвером хранилища (IndexedDB, WebSQL или localStorage).


Фильтрация данных во время обхода

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

const filtered = [];

await localforage.iterate((value, key) => {
  if (key.startsWith('cache_')) {
    filtered.push(value);
  }
});

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

Дополнительный вариант с условной логикой:

const activeItems = [];

await localforage.iterate((value) => {
  if (value && value.active) {
    activeItems.push(value);
  }
});

Формирование структуры Map

Для случаев, где важен быстрый доступ по ключу, результат iterate часто преобразуется в Map:

const map = new Map();

await localforage.iterate((value, key) => {
  map.set(key, value);
});

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

  • сохранять соответствие ключ–значение
  • обеспечивать O(1)-доступ к элементам
  • упрощать дальнейшие операции поиска и обновления

Преобразование данных в индексированную структуру

При необходимости сохранения порядка итерации вместе с ключами формируется индексированный массив:

const indexed = [];

await localforage.iterate((value, key, index) => {
  indexed[index - 1] = { key, value };
});

Использование iterationNumber позволяет точно контролировать позицию элемента в итоговой структуре.


Асинхронная обработка и ограничения накопления

Несмотря на то что iterate поддерживает async/await внутри колбэка, возврат промисов не влияет на сам процесс обхода:

await localforage.iterate(async (value, key) => {
  const processed = await someAsyncTransform(value);
  console.log(key, processed);
});

Однако порядок выполнения асинхронных операций внутри колбэка не гарантирует параллельности; итерация остаётся последовательной.

Важно учитывать, что накопление результатов через асинхронные операции может приводить к увеличению времени полного прохода.


Ограничения на прерывание итерации

Прямого механизма досрочного выхода из iterate не предусмотрено. Конструкция:

await localforage.iterate((value, key) => {
  if (key === 'target') {
    return;
  }
});

не останавливает цикл, а лишь завершает текущий вызов колбэка.

Попытки имитации прерывания обычно сводятся к генерации ошибки:

try {
  await localforage.iterate((value, key) => {
    if (key === 'stop') {
      throw new Error('break');
    }
  });
} catch (e) {
  // обработка завершения
}

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


Получение результата через внешнюю агрегацию

Наиболее универсальный паттерн построения результата заключается в вынесении состояния за пределы колбэка:

function collectAll() {
  const state = {
    entries: [],
    count: 0
  };

  return localforage.iterate((value, key) => {
    state.entries.push([key, value]);
    state.count++;
  }).then(() => state);
}

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

  • сохранять несколько агрегированных показателей
  • комбинировать разные типы данных
  • возвращать единый объект после завершения обхода

Использование iterate для построения выборок

Типичный сценарий получения результата — построение выборки с несколькими условиями:

const selection = [];

await localforage.iterate((value, key) => {
  if (key.startsWith('user_') && value?.enabled) {
    selection.push(value);
  }
});

Подобная модель заменяет комбинацию keys() + getItem() и уменьшает количество обращений к хранилищу.


Преобразование данных в агрегированные структуры

iterate часто используется для вычисления сводных значений:

let sum = 0;

await localforage.iterate((value) => {
  if (typeof value === 'number') {
    sum += value;
  }
});

Другой вариант — группировка по категориям:

const groups = {};

await localforage.iterate((value) => {
  const type = value.type || 'unknown';

  if (!groups[type]) {
    groups[type] = [];
  }

  groups[type].push(value);
});

Поведение при пустом хранилище

Если в хранилище отсутствуют данные, iterate завершается мгновенно, не вызывая колбэк ни одного раза. При этом возвращаемое значение остаётся undefined, если не используется внешняя обёртка.

await localforage.iterate(() => {
  // не выполнится
});

Особенности работы с порядком обхода

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

  • IndexedDB — порядок ключей по возрастанию
  • WebSQL — порядок вставки не гарантируется
  • localStorage — порядок ключей может отличаться между реализациями

Следовательно, построение результата через массив или индексированную структуру не должно полагаться на стабильную сортировку без дополнительной логики.


Комбинирование iterate с другими API

Результаты iterate часто используются совместно с другими методами:

const keys = [];

await localforage.iterate((_, key) => {
  keys.push(key);
});

const values = await Promise.all(
  keys.map(k => localforage.getItem(k))
);

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


Типовые ошибки при извлечении результата

Наиболее распространённая ошибка — попытка трактовать iterate как функцию, возвращающую массив:

const data = await localforage.iterate(() => {});
// data не содержит список элементов

Другой частый случай — ожидание синхронного поведения:

const result = [];

localforage.iterate((v) => {
  result.push(v);
});

console.log(result); // может быть пустым

Корректная модель всегда опирается на завершение промиса.