Универсальная обёртка с обработкой ошибок

idb-keyval — это небольшая и удобная библиотека для работы с IndexedDB, предоставляющая простые методы для хранения и получения данных. В отличие от низкоуровневого API IndexedDB, idb-keyval позволяет оперировать данными через ключ-значение, что значительно упрощает работу с асинхронным хранилищем.

Базовые методы библиотеки включают:

  • set(key, value) — сохраняет значение по указанному ключу.
  • get(key) — получает значение по ключу.
  • del(key) — удаляет значение по ключу.
  • clear() — очищает всё хранилище.
  • keys() — возвращает массив всех ключей.
  • entries() — возвращает массив пар [ключ, значение].

Каждый метод возвращает промис, что позволяет использовать их с async/await.


Создание универсальной обёртки

Универсальная обёртка обеспечивает:

  1. Обработку ошибок при любом взаимодействии с IndexedDB.
  2. Логирование проблем, если доступ к данным невозможен.
  3. Стандартизированный интерфейс для всех операций хранения.

Пример структуры универсальной обёртки:

import { set, get, del, clear, keys } from 'idb-keyval';

const storage = {
    async setItem(key, value) {
        try {
            await set(key, value);
            console.log(`Успешно сохранено: ${key}`);
        } catch (error) {
            console.error(`Ошибка при сохранении ключа "${key}":`, error);
        }
    },

    async getItem(key) {
        try {
            const value = await get(key);
            if (value === undefined) {
                console.warn(`Ключ "${key}" не найден`);
                return null;
            }
            return value;
        } catch (error) {
            console.error(`Ошибка при получении ключа "${key}":`, error);
            return null;
        }
    },

    async removeItem(key) {
        try {
            await del(key);
            console.log(`Ключ "${key}" удалён`);
        } catch (error) {
            console.error(`Ошибка при удалении ключа "${key}":`, error);
        }
    },

    async clearAll() {
        try {
            await clear();
            console.log('Хранилище очищено');
        } catch (error) {
            console.error('Ошибка при очистке хранилища:', error);
        }
    },

    async listKeys() {
        try {
            return await keys();
        } catch (error) {
            console.error('Ошибка при получении списка ключей:', error);
            return [];
        }
    }
};

Обработка ошибок

Ключевой момент при работе с IndexedDB — это непредсказуемые ошибки, связанные с ограничениями браузера, блокировкой IndexedDB или нехваткой места. Универсальная обёртка должна:

  • Возвращать значения по умолчанию при ошибках, чтобы приложение не ломалось.
  • Логировать все ошибки с указанием ключа и операции.
  • Не выбрасывать исключения наружу, если это не критично.

Пример обработки ошибок при записи:

async function safeSet(key, value) {
    try {
        await set(key, value);
        return true;
    } catch (err) {
        console.error(`Не удалось записать ключ "${key}":`, err);
        return false;
    }
}

Поддержка сложных типов данных

idb-keyval позволяет хранить любые сериализуемые объекты, включая массивы, объекты и даже вложенные структуры. Универсальная обёртка может добавлять дополнительное преобразование:

async function setJSON(key, obj) {
    try {
        const serialized = JSON.stringify(obj);
        await set(key, serialized);
    } catch (error) {
        console.error(`Ошибка при сохранении объекта под ключом "${key}":`, error);
    }
}

async function getJSON(key) {
    try {
        const serialized = await get(key);
        if (serialized === undefined) return null;
        return JSON.parse(serialized);
    } catch (error) {
        console.error(`Ошибка при чтении объекта под ключом "${key}":`, error);
        return null;
    }
}

Это позволяет безопасно хранить сложные структуры данных без потери информации.


Расширение функциональности

Дополнительные возможности универсальной обёртки:

  1. Версионирование данных — хранение даты изменения или версии объекта.
  2. Кэширование в памяти — ускорение частого доступа к данным.
  3. Тайм-ауты на операции — защита от зависаний при недоступной IndexedDB.

Пример кэширования:

const memoryCache = new Map();

async function getCached(key) {
    if (memoryCache.has(key)) return memoryCache.get(key);
    const value = await get(key);
    memoryCache.set(key, value);
    return value;
}

Итоговые рекомендации по дизайну

  • Всегда использовать try/catch при асинхронных операциях с IndexedDB.
  • Возвращать безопасные значения (например, null или пустой массив) при ошибках.
  • Обеспечивать логирование, чтобы понимать причину сбоев.
  • Разделять простые операции и работу с объектами, используя сериализацию при необходимости.
  • Использовать кэширование и версионирование для улучшения производительности и устойчивости.

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