AbortError в контексте IndexedDB и Dexie.js возникает в момент, когда транзакция или операция прерывается до завершения по причинам, не связанным с логической ошибкой бизнес-уровня. Это особый тип сбоя, который сигнализирует не о некорректных данных, а о том, что выполнение цепочки операций было насильственно остановлено окружением, пользователем или самим браузером.
В отличие от ConstraintError, VersionError
или QuotaExceededError, которые описывают нарушения правил
базы данных, AbortError относится к категории жизненного цикла
транзакции. Он почти всегда означает: транзакция не дошла до этапа
commit и была откатана.
В основе Dexie.js лежит IndexedDB, где каждая операция выполняется в рамках транзакции. Транзакция может завершиться тремя путями:
AbortError появляется именно в третьем сценарии.
Типичные причины на уровне браузера:
transaction.abort() (напрямую или косвенно через
Dexie);AbortController, разрывающего цепочку
асинхронных операций.Важно, что IndexedDB не гарантирует завершение долгих транзакций: она оптимизирована под короткие, атомарные операции.
Dexie.js расширяет модель IndexedDB, добавляя удобный API:
db.transaction('rw', db.users, db.orders, async () => {
const user = await db.users.get(1);
await db.orders.add({ userId: user.id, total: 100 });
});
Внутри такой транзакции Dexie удерживает контекст до завершения функции. Если внутри возникает AbortError, транзакция помечается как прерванная и все изменения откатываются.
Ключевая особенность: Dexie автоматически связывает промисы с жизненным циклом транзакции. Это означает, что любая асинхронная операция внутри блока может привести к abort, если внешний контекст исчез.
Наиболее простой сценарий — потеря страницы:
В этом случае IndexedDB уничтожает активные транзакции, возвращая AbortError.
Dexie позволяет вручную прервать транзакцию:
const tx = db.transaction('rw', db.items, async () => {
db.items.add({ name: 'A' });
tx.abort();
});
После вызова abort() любые дальнейшие операции внутри
транзакции будут отклонены с AbortError.
IndexedDB использует блокировочную модель. Если одна транзакция удерживает store, другая может быть поставлена в ожидание. При длительном ожидании браузер иногда завершает транзакцию с abort, чтобы избежать дедлока.
Особенно часто это проявляется при:
Dexie поддерживает интеграцию с AbortController:
const controller = new AbortController();
db.transaction('rw', db.logs, async () => {
await db.logs.bulkAdd(data, { signal: controller.signal });
});
При вызове:
controller.abort();
операции Dexie получают сигнал отмены, и транзакция завершается с AbortError.
Когда IndexedDB выбрасывает AbortError:
on('error'), catch)
получают событие ошибки.Особенность Dexie заключается в том, что AbortError часто “распространяется” вверх по цепочке промисов, даже если исходная операция была частично завершена.
AbortError часто путают с:
TransactionInactiveError — когда операция выполняется
вне активной транзакции;InvalidStateError — когда объект базы находится в
неправильном состоянии;Ключевое отличие AbortError:
Одна из сложных проблем Dexie — асинхронные разрывы внутри транзакции.
db.transaction('rw', db.items, async () => {
const items = await fetch('/api/items').then(r => r.json());
await db.items.bulkAdd(items);
});
Если внешний await fetch() занимает слишком много
времени, транзакция может выйти за пределы допустимого окна активности
IndexedDB. Браузер в этот момент может считать транзакцию “зависшей” и
завершить её abort’ом.
Dexie в таких случаях не может восстановить контекст — транзакция уже разрушена.
AbortError часто является временным. Особенно при:
Типовой паттерн повторной попытки:
async function safeWrite(data, retries = 3) {
try {
return await db.transaction('rw', db.items, async () => {
await db.items.add(data);
});
} catch (e) {
if (e.name === 'AbortError' && retries > 0) {
return safeWrite(data, retries - 1);
}
throw e;
}
}
Однако retry требует осторожности: повтор транзакции должен быть идемпотентным, иначе возможны дубли записей.
Bulk-операции особенно чувствительны к abort:
await db.items.bulkPut(largeArray);
При:
операция может завершиться AbortError без частичного commit.
Dexie гарантирует атомарность транзакции: либо все элементы записаны, либо ни один.
Hooks Dexie (creating, updating,
deleting) могут косвенно вызывать abort, если внутри них
происходит асинхронная логика, нарушающая жизненный цикл транзакции.
db.items.hook('creating', async (primKey, obj, tx) => {
await fetch('/validate'); // потенциальный риск
});
Если hook задерживает транзакцию, браузер может завершить её AbortError.
Критично избегать длинных RW-транзакций:
Транзакция должна содержать только IndexedDB операции:
Использование AbortController должно быть контролируемым:
При массовых операциях важно учитывать конкуренцию:
Одна прерванная транзакция может влиять на другие через:
Dexie пытается изолировать транзакции, но IndexedDB модель накладывает ограничения: если один writer держит lock слишком долго, другие операции могут получить AbortError как побочный эффект.
При отладке важно учитывать:
Логи Dexie можно использовать для выявления проблемных транзакций:
Dexie.debug = true;
Каждый из этих паттернов увеличивает вероятность AbortError в условиях реальной нагрузки браузера.