Библиотека localForage полностью построена вокруг асинхронной модели работы с данными. Практически каждый её метод возвращает объект Promise, благодаря чему операции чтения, записи, удаления и перебора данных выполняются без блокировки основного потока браузера.
До широкого распространения синтаксиса async/await
основным способом обработки результатов асинхронных операций была
цепочка методов then() и catch(). Несмотря на
популярность современных конструкций, данный подход по-прежнему активно
используется в существующих проектах, библиотеках и фреймворках.
Методы then() и catch() позволяют:
Практически любой метод localForage возвращает Promise:
localforage.setItem("user", {
name: "Alex"
});
На первый взгляд кажется, что запись выполняется мгновенно. На самом деле метод немедленно возвращает Promise, а сама операция сохранения завершается позже.
Упрощённо это выглядит следующим образом:
const promise = localforage.setItem("user", {
name: "Alex"
});
console.log(promise);
В консоли можно увидеть объект Promise, который находится в одном из состояний:
pending — операция ещё выполняется;fulfilled — операция завершилась успешно;rejected — произошла ошибка.Именно для обработки этих состояний используются методы
then() и catch().
После успешного сохранения данных вызывается функция, переданная в
then().
Пример:
localforage
.setItem("username", "admin")
.then(function(value) {
console.log("Сохранено:", value);
});
Результат:
Сохранено: admin
Особенность метода setItem() заключается в том, что в
then() возвращается сохранённое значение.
Это позволяет сразу использовать полученный объект:
localforage
.setItem("settings", {
theme: "dark",
language: "ru"
})
.then(function(settings) {
console.log(settings.theme);
});
Вывод:
dark
При чтении данных в обработчик передаётся найденное значение.
Пример:
localforage
.getItem("username")
.then(function(value) {
console.log(value);
});
Если запись существует:
admin
Если ключ отсутствует:
null
Поэтому часто выполняют дополнительную проверку:
localforage
.getItem("username")
.then(function(value) {
if (value === null) {
console.log("Пользователь не найден");
return;
}
console.log(value);
});
Метод catch() вызывается при возникновении ошибки в
Promise.
Общий шаблон:
localforage
.getItem("user")
.then(function(value) {
console.log(value);
})
.catch(function(error) {
console.error(error);
});
Если операция завершилась успешно, catch() не будет
вызван.
Если произошла ошибка, управление перейдёт в него автоматически.
Одним из главных преимуществ Promise является возможность создавать цепочки операций.
Пример:
localforage
.setItem("counter", 10)
.then(function() {
return localforage.getItem("counter");
})
.then(function(value) {
console.log(value);
});
Порядок выполнения:
Результат:
10
Каждый then() может возвращать значение, которое
автоматически попадёт в следующий обработчик.
Пример:
localforage
.getItem("user")
.then(function(user) {
return user.name;
})
.then(function(name) {
console.log(name);
});
Если в хранилище находится объект:
{
name: "Alex",
age: 30
}
то вывод будет:
Alex
Такой механизм позволяет постепенно преобразовывать данные в процессе цепочки.
Наиболее распространённый сценарий — запуск следующей асинхронной операции.
Пример:
localforage
.getItem("user")
.then(function(user) {
return localforage.getItem("settings");
})
.then(function(settings) {
console.log(settings);
});
Когда из then() возвращается Promise, следующий
обработчик автоматически ожидает его завершения.
Это называется «сплющиванием» цепочки Promise.
Рассмотрим последовательную работу с несколькими ключами.
localforage
.setItem("name", "Alex")
.then(function() {
return localforage.setItem("age", 30);
})
.then(function() {
return localforage.setItem("city", "Moscow");
})
.then(function() {
console.log("Все данные сохранены");
})
.catch(function(error) {
console.error(error);
});
Каждая операция начинается только после завершения предыдущей.
Любая ошибка внутри цепочки автоматически передаётся в ближайший
catch().
Пример:
localforage
.getItem("user")
.then(function(user) {
console.log(user.name);
})
.catch(function(error) {
console.error("Ошибка:", error);
});
Если значение окажется равным null, попытка обращения к
свойству может привести к исключению.
Ошибка будет перехвачена:
Ошибка: TypeError
Иногда необходимо самостоятельно прервать выполнение цепочки.
Пример:
localforage
.getItem("user")
.then(function(user) {
if (!user) {
throw new Error("Пользователь отсутствует");
}
return user;
})
.then(function(user) {
console.log(user.name);
})
.catch(function(error) {
console.error(error.message);
});
Вывод:
Пользователь отсутствует
Количество обработчиков не ограничено.
Пример:
localforage
.getItem("price")
.then(function(price) {
return price * 1.2;
})
.then(function(priceWithTax) {
return Math.round(priceWithTax);
})
.then(function(result) {
console.log(result);
});
Каждый этап отвечает за отдельное преобразование данных.
Такой подход делает код более читаемым и поддерживаемым.
После обработки ошибки цепочка может продолжиться.
Пример:
localforage
.getItem("settings")
.catch(function() {
return {
theme: "light"
};
})
.then(function(settings) {
console.log(settings.theme);
});
Если произошла ошибка, вместо неё будет использован резервный объект.
Метод then() может принимать два обработчика:
localforage.getItem("user").then(
function(result) {
console.log(result);
},
function(error) {
console.error(error);
}
);
Однако такой стиль считается менее удобным.
Гораздо чаще используется конструкция:
localforage
.getItem("user")
.then(function(result) {
console.log(result);
})
.catch(function(error) {
console.error(error);
});
Она обеспечивает более понятную структуру и лучше подходит для длинных цепочек.
Удаление данных также возвращает Promise.
Пример:
localforage
.removeItem("token")
.then(function() {
console.log("Токен удалён");
})
.catch(function(error) {
console.error(error);
});
Поскольку метод ничего не возвращает, параметр обработчика обычно не используется.
Полная очистка хранилища выполняется аналогичным образом.
localforage
.clear()
.then(function() {
console.log("Хранилище очищено");
})
.catch(function(error) {
console.error(error);
});
Очистка считается завершённой только после успешного выполнения Promise.
Метод length() возвращает Promise с числом
элементов.
localforage
.length()
.then(function(count) {
console.log(count);
});
Результат:
15
Полученное число можно использовать в последующих обработчиках:
localforage
.length()
.then(function(count) {
return count * 2;
})
.then(function(result) {
console.log(result);
});
Для выполнения нескольких операций одновременно часто применяется
Promise.all().
Пример:
Promise.all([
localforage.getItem("user"),
localforage.getItem("settings"),
localforage.getItem("token")
])
.then(function(values) {
console.log(values);
})
.catch(function(error) {
console.error(error);
});
В массиве values будут находиться результаты всех
запросов:
[
user,
settings,
token
]
Все Promise должны завершиться успешно. Если хотя бы один завершится
ошибкой, управление перейдёт в catch().
Неправильно:
localforage
.getItem("user")
.then(function() {
localforage.getItem("settings");
})
.then(function(settings) {
console.log(settings);
});
Следующий обработчик не дождётся завершения операции.
Правильно:
localforage
.getItem("user")
.then(function() {
return localforage.getItem("settings");
})
.then(function(settings) {
console.log(settings);
});
Нежелательно:
localforage
.setItem("data", value)
.then(function() {
console.log("OK");
});
Предпочтительно:
localforage
.setItem("data", value)
.then(function() {
console.log("OK");
})
.catch(function(error) {
console.error(error);
});
Неправильно:
const value = localforage.getItem("user");
console.log(value);
В переменной окажется Promise, а не данные.
Правильно:
localforage
.getItem("user")
.then(function(value) {
console.log(value);
});
Наиболее уместно применение цепочек Promise в следующих ситуациях:
async/await;Методы then() и catch() являются
фундаментальным механизмом взаимодействия с localForage, поскольку
позволяют полностью контролировать жизненный цикл асинхронных операций
хранения данных и обеспечивают предсказуемую обработку как успешных
результатов, так и возникающих ошибок.