Сигнатура колбэка iterate

Метод iterate в localForage предназначен для последовательного обхода всех ключ–значение пар в хранилище. Его поведение определяется не только самим методом, но и строгой сигнатурой callback-функции, через которую происходит доступ к данным на каждой итерации.

Базовая форма вызова выглядит следующим образом:

localforage.iterate(iteratorCallback, successCallback);

или в расширенном виде с обработкой ошибки через Promise:

localforage.iterate(iteratorCallback)
  .then(successCallback)
  .catch(errorCallback);

Основная сигнатура iteratorCallback

Главный параметр метода iterate — это функция-итератор, которая вызывается для каждого элемента хранилища.

function iteratorCallback(value, key, iterationNumber) {
  // логика обработки каждого элемента
}

Параметры callback-функции

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

key Строковый ключ текущего элемента. Используется для идентификации записи в хранилище. Позволяет связывать значение с его оригинальным идентификатором.

iterationNumber Порядковый номер текущей итерации, начиная с 1. Важно учитывать, что это не индекс массива, а логический счётчик прохода по записям.


Контекст выполнения callback

Функция iteratorCallback вызывается в контексте, привязанном к экземпляру localForage:

this === localforageInstance

Это позволяет внутри callback обращаться к методам экземпляра, например:

localforage.iterate(function (value, key, iterationNumber) {
  console.log(this.driver()); // доступ к текущему драйверу
});

Контекст полезен при необходимости динамически взаимодействовать с настройками хранилища во время обхода.


Сигнатура successCallback

После завершения итерации (когда все записи обработаны), может быть вызван второй callback:

function successCallback() {
  // выполнение после завершения iterate
}

Он не получает аргументов и служит сигналом полного завершения обхода. Однако в современных реализациях чаще используется Promise:

localforage.iterate(iteratorCallback).then(() => {
  // завершение обхода
});

Поведение возвращаемого значения iteratorCallback

Возвращаемое значение iteratorCallback игнорируется механизмом iterate. Никакое значение, возвращённое из функции, не влияет на поток выполнения или результат метода.

localforage.iterate((value, key) => {
  return value; // не используется библиотекой
});

Порядок итерации

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

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

Следовательно, iterationNumber отражает порядок обхода, но не обязательно порядок вставки данных.


Ограничения управления потоком

В iterate отсутствует встроенный механизм досрочного выхода через break или return false.

Типичное поведение:

  • возвращаемые значения игнорируются
  • нет официального API для остановки итерации

Единственный способ прервать выполнение — генерация ошибки:

localforage.iterate((value, key, iterationNumber) => {
  if (key === "stopKey") {
    throw new Error("Прерывание итерации");
  }
});

Это приводит к переходу в catch и прекращению обхода.


Асинхронная модель выполнения

Несмотря на синхронную форму callback, сам iterate работает асинхронно:

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

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


Особенности передачи значений

При использовании сериализации (например, JSON):

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

Пример:

localforage.setItem("user", { name: "Alex", age: 30 });

localforage.iterate((value, key) => {
  // value будет объектом { name: "Alex", age: 30 }
});

Стабильность и совместимость сигнатуры

Сигнатура:

(value, key, iterationNumber)

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


Типизация (TypeScript-представление)

Для строгих сред можно представить сигнатуру следующим образом:

type IterateCallback<T> = (
  value: T,
  key: string,
  iterationNumber: number
) => void;

Метод:

iterate<T>(
  iteratorCallback: IterateCallback<T>
): Promise<void>;

Контекст ошибок и исключений

Если внутри iteratorCallback возникает исключение:

  • итерация немедленно прекращается
  • Promise переходит в состояние rejection
  • последующие элементы не обрабатываются

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


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

Если хранилище не содержит записей:

  • iteratorCallback не вызывается ни разу
  • successCallback (или .then) выполняется сразу
localforage.iterate(() => {
  // не выполнится
}).then(() => {
  // выполнится сразу
});

Взаимодействие с удалением и изменением данных

Изменение данных во время итерации:

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

Это делает iterate чувствительным к конкурентным изменениям хранилища.


Практическая модель сигнатуры в работе

Фактическая модель вызова можно представить так:

for (let i = 0; i < storage.length; i++) {
  iteratorCallback(value, key, i + 1);
}

Но в реальности реализация абстрагирована и зависит от backend-хранилища, а не от простого массива.


Итоговая структура сигнатуры

Ключевая форма, которую следует фиксировать при работе:

localforage.iterate(function (value, key, iterationNumber) {
  // обработка элемента
}, function () {
  // завершение итерации
});

или Promise-вариант:

localforage.iterate((value, key, iterationNumber) => {
  // обработка
}).then(() => {
  // завершено
});