Глобальный обработчик ошибок

Dexie.js построен поверх IndexedDB и полностью наследует её модель ошибок, при этом добавляя собственный слой нормализации и типизации исключений. В реальных приложениях работа с IndexedDB неизбежно сопровождается различными сбоями: конфликтами транзакций, ошибками квот, нарушениями схемы, отменами операций и асинхронными сбоями. Поэтому централизованная обработка ошибок становится ключевым элементом архитектуры хранилища данных.

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


Природа ошибок в Dexie.js

Ошибки в Dexie можно условно разделить на несколько категорий:

  • ошибки IndexedDB (DOMException);
  • ошибки транзакций;
  • ошибки схемы базы данных;
  • ошибки квот браузера;
  • пользовательские ошибки, выброшенные в цепочке промисов;
  • ошибки операций (например, нарушение уникальности ключа).

Dexie оборачивает большинство ошибок в собственный объект DexieError, сохраняя исходную причину в поле inner.

Ключевой момент: любая операция Dexie возвращает Promise, а значит, ошибка всегда проходит через promise-цепочку и может быть перехвачена централизованно.


Глобальный перехват через Dexie.on(“error”)

Основной механизм глобальной обработки ошибок — событие уровня библиотеки:

Dexie.on("error", (error) => {
    console.error("Глобальная ошибка Dexie:", error);
});

Этот обработчик вызывается при любой необработанной ошибке, которая не была перехвачена локально в .catch() или внутри транзакции.

Поведение глобального обработчика

  • срабатывает только для необработанных ошибок;
  • не перехватывает ошибки, уже обработанные локально;
  • получает объект ошибки с нормализованной структурой Dexie;
  • не влияет на поток выполнения автоматически.

Важно понимать: это не аналог window.onerror. Dexie работает на уровне своих промисов и транзакций.


Глобальные ошибки через Dexie.on(“blocked”)

Отдельный тип глобального события связан с конфликтами версий базы:

Dexie.on("blocked", () => {
    console.warn("База данных заблокирована другой вкладкой");
});

Хотя это не ошибка в классическом смысле, поведение приложения при блокировке часто обрабатывается вместе с глобальными ошибками.


Dexie.on(“versionchange”) и ошибки миграции

При обновлении схемы базы возможны сбои миграции. Dexie позволяет отслеживать изменения версии:

Dexie.on("versionchange", (event) => {
    console.warn("Версия базы данных изменилась извне");
});

Ошибки миграции чаще всего связаны с:

  • несовместимостью схем;
  • прерыванием upgrade-транзакции;
  • конфликтом версий между вкладками.

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


Использование db.on(“error”)

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

const db = new Dexie("MyDatabase");

db.on("error", (error) => {
    console.error("Ошибка базы данных:", error);
});

Этот обработчик более специфичен и применяется только к одной базе. В сложных приложениях это предпочтительнее глобального Dexie.on(“error”), так как позволяет разделять контексты.


Ошибки транзакций и их глобальная обработка

Dexie использует транзакционную модель IndexedDB. Ошибка внутри транзакции приводит к её автоматическому откату.

db.transaction("rw", db.users, async () => {
    await db.users.add({ id: 1, name: "Alex" });
    await db.users.add({ id: 1, name: "Duplicate" });
});

В этом случае возникает ошибка уникальности ключа, и вся транзакция откатывается.

Если ошибка не перехвачена внутри .catch(), она попадёт в глобальный обработчик.


Приоритет локальной и глобальной обработки

Dexie следует строгому правилу приоритета:

  1. локальный .catch() на запросе;
  2. обработка внутри transaction().catch();
  3. db.on("error");
  4. Dexie.on("error").

Если ошибка обработана на более низком уровне, она не всплывает вверх.


Типизация ошибок DexieError

Dexie нормализует ошибки в единый формат:

  • name — тип ошибки;
  • message — описание;
  • inner — исходная ошибка IndexedDB;
  • stack — стек вызовов;
  • дополнительные поля в зависимости от типа ошибки.

Пример обработки:

db.on("error", (error) => {
    if (error.name === "QuotaExceededError") {
        console.warn("Превышена квота хранения");
    }
});

QuotaExceededError и глобальная стратегия

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

Dexie не может автоматически освободить место, поэтому глобальный обработчик часто используется для:

  • логирования состояния базы;
  • отключения записи новых данных;
  • переключения на режим “только чтение”.
Dexie.on("error", (error) => {
    if (error.name === "QuotaExceededError") {
        console.error("Хранилище переполнено");
    }
});

AbortError и отмена операций

AbortError возникает при отмене транзакции или запроса:

db.on("error", (error) => {
    if (error.name === "AbortError") {
        // операция отменена системой или пользователем
    }
});

В Dexie AbortError часто появляется при:

  • закрытии вкладки;
  • конфликте транзакций;
  • ручной отмене цепочки промисов.

VersionError и проблемы схемы

При несовместимости версий базы возникает ошибка инициализации:

Dexie.on("error", (error) => {
    if (error.name === "VersionError") {
        console.error("Ошибка версии базы данных");
    }
});

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


ConstraintError и нарушения ограничений

Dexie транслирует IndexedDB ConstraintError при нарушении индексов:

  • уникальные ключи;
  • составные индексы;
  • ограничения схемы.
db.on("error", (error) => {
    if (error.name === "ConstraintError") {
        console.warn("Нарушение ограничения индекса");
    }
});

Ошибки в цепочках async/await

Dexie полностью поддерживает async/await, но ошибки внутри асинхронных функций могут не обрабатываться локально:

db.on("error", (error) => {
    console.error("Необработанная ошибка:", error);
});

async function run() {
    await db.users.add({ id: 1 });
    await db.users.add({ id: 1 });
}

Если run() не окружена try/catch, ошибка попадёт в глобальный обработчик.


Логирование и восстановление состояния

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

  • повторные попытки операций;
  • переключение на fallback-хранилище;
  • очистка повреждённых транзакций;
  • сигнализация UI о деградации состояния.

Dexie не навязывает стратегию восстановления, но предоставляет полный доступ к контексту ошибки.


Особенности поведения в разных браузерах

Поведение глобальных ошибок может отличаться:

  • Chrome чаще возвращает DOMException с расширенными полями;
  • Firefox может нормализовать сообщения иначе;
  • Safari иногда агрессивно завершает транзакции с AbortError.

Dexie выравнивает различия, но глобальный обработчик остаётся ключевой точкой унификации поведения.


Роль глобального обработчика в архитектуре приложения

Глобальная обработка ошибок в Dexie.js выполняет роль последнего уровня защиты:

  • фиксирует необработанные сбои;
  • предотвращает “тихие падения” промисов;
  • обеспечивает централизованную диагностику;
  • помогает выявлять ошибки схемы и логики доступа к данным.

При этом он не заменяет локальную обработку, а дополняет её, формируя многоуровневую систему устойчивости работы с IndexedDB.