Метод each() в Dexie.js предназначен для
последовательного обхода всех элементов выборки, сформированной через
Collection. Он используется в случаях, когда требуется
выполнить побочный эффект для каждого объекта без необходимости заранее
материализовывать весь результат в массив.
each() применяется к экземпляру Collection,
который формируется после операций where(),
filter(), orderBy(), limit() и
других методов Dexie.
db.users.where('age').above(18).each(user => {
console.log(user.name);
});
Ключевая особенность заключается в том, что данные не загружаются целиком. Итерация происходит по мере чтения из IndexedDB, что снижает потребление памяти при больших выборках.
collection.each(callback)
callback получает текущий элемент коллекции и, при
необходимости, объект курсора.
collection.each((value, cursor) => {
// value — объект из таблицы
// cursor — внутренний IndexedDB cursor (опционально)
});
Основные характеристики:
Promise<void> после завершения
обходаasync внутри callbackПорядок зависит от источника Collection:
orderBy(index) — строго упорядоченный обход по
индексуwhere(index) без сортировки — порядок индексаreverse() — инверсия порядкаlimit() — ограничение количества элементовdb.users.orderBy('age').each(user => {
console.log(user.age);
});
each() часто применяется после фильтров
where() и filter().
db.orders.where('status').equals('pending').each(order => {
console.log(order.id);
});
db.orders
.filter(order => order.total > 1000)
.each(order => {
console.log(order.total);
});
Важно учитывать, что filter() выполняется на стороне
клиента после извлечения данных, поэтому each() будет
вызываться только для уже отфильтрованных элементов.
each() поддерживает механизм прерывания обхода через
возврат false из callback.
db.users.each(user => {
if (user.age > 30) {
return false;
}
console.log(user.name);
});
При возврате false дальнейшее чтение из IndexedDB
прекращается. Это позволяет экономить ресурсы при поиске первого
подходящего элемента или при частичной обработке данных.
Callback each() не ожидает Promise. Это
важное ограничение, которое часто приводит к ошибкам архитектуры.
db.users.each(async user => {
await fetch('/log', {
method: 'POST',
body: JSON.stringify(user)
});
});
В данном случае each() не дожидается завершения
fetch, и выполнение продолжается параллельно.
Корректный подход при необходимости асинхронной обработки:
const users = await db.users.toArray();
for (const user of users) {
await fetch('/log', {
method: 'POST',
body: JSON.stringify(user)
});
}
Или при необходимости потоковой обработки без накопления массива —
использование each() допускается только для синхронных
операций.
each() и toArray() решают разные
задачи:
const users = await db.users.toArray();
users.forEach(u => console.log(u.name));
await db.users.each(u => console.log(u.name));
При работе с большими наборами данных each()
демонстрирует более стабильное потребление памяти, так как данные не
материализуются полностью.
Факторы влияния:
where() с индексированными полямиfilter() (замедляет выполнение)orderBy() требует дополнительных
операций)Эффективность each() напрямую зависит от индексации.
db.users.where('email').equals('test@example.com').each(user => {
console.log(user);
});
При наличии индекса операция выполняется через IndexedDB cursor без полного сканирования таблицы.
each() завершает цепочку методов и не допускает
дальнейшего chaining.
db.users
.where('age').above(18)
.each(user => {
console.log(user);
})
.orderBy('name'); // не применяется
После each() дальнейшие методы коллекции не имеют
смысла, так как итерация уже началась.
В некоторых сценариях требуется доступ к низкоуровневому курсору IndexedDB.
db.users.each((user, cursor) => {
console.log(cursor.primaryKey);
});
Курсор позволяет:
При обработке больших объемов данных важно учитывать:
return falseПример потоковой обработки:
let count = 0;
await db.logs.each(log => {
if (log.level === 'error') {
count++;
}
if (count > 100) {
return false;
}
});
each() выполняется в контексте текущей транзакции
Dexie.
db.transaction('r', db.users, async () => {
await db.users.each(user => {
console.log(user.id);
});
});
При завершении транзакции чтение данных может быть автоматически зафиксировано в зависимости от режима доступа.
await как механизм
синхронизации| Метод | Память | Асинхронность | Результат |
|---|---|---|---|
| each() | низкая | нет | void |
| toArray() | высокая | да | array |
| filter() | средняя | да | collection |
| reverse().each() | низкая | нет | void |
each() используется в сценариях:
Внутренняя реализация Dexie опирается на IndexedDB cursor API, что означает:
Если во время обхода изменяются записи той же таблицы:
Метод возвращает Promise, который резолвится после
завершения обхода или после досрочного прерывания.
await db.users.each(user => {
console.log(user.name);
});
Это позволяет синхронизировать поток выполнения с завершением обхода без накопления результатов.