Асинхронность с использованием callback-функций
Рассмотрим асинхронный запрос на сервер с использованием callback-функций:
console.log('request data'); // Симулируем отправку запроса
setTimeout(() => {
console.log('preparing data'); // Сервер готовит данные (2 секунды)
const backendData = { server: 'localhost', port: 2000, status: 'working' };
setTimeout(() => {
console.log('data received');
backendData.modified = true; // Модифицируем данные
console.log(backendData); // Выводим полученные данные
}, 2000); // Отправка данных клиенту (2 секунды)
}, 2000);
Такой подход приводит к вложенности callback-функций. При увеличении количества асинхронных операций код становится сложным в поддержке и чтении.
Promise: решение проблемы вложенности
Promise решает проблему вложенности callback-функций при работе с асинхронными операциями.
Пример создания Promise:
const p = new Promise((resolve, reject) => {
console.log('request data');
setTimeout(() => {
const backendData = { server: 'localhost', port: 2000, status: 'working' };
resolve(backendData); // Вызываем resolve при успешном завершении
}, 2000);
});
resolve – функция, вызываемая при успешном завершении асинхронной операции. Она принимает данные для дальнейшей передачи.
Для обработки данных используется метод .then():
p.then((data) => {
console.log('promise resolve');
console.log(data); // Выводим данные
});
Метод .then() принимает callback-функцию, вызываемую после успешного выполнения Promise.
Более сложный пример с Promise
Добавим ещё одну асинхронную операцию:
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
data.modified = true;
resolve(data);
}, 2000);
});
p.then((data) => {
return p2; // Возвращаем новый Promise
}).then((clientData) => {
console.log('data received');
console.log(clientData);
});
Возвращение нового Promise из первого .then() позволяет избежать вложенности и улучшает читаемость кода.
Цепочки Promise (Chaining)
Возвращение Promise из .then() позволяет создавать цепочки Promise, упрощая работу с последовательными асинхронными операциями. Вместо вложенных .then(), используется одна цепочка.
Обработка ошибок с помощью .catch()
Для обработки ошибок используется метод .catch():
p.then((data) => {
// ...
}).catch((error) => {
console.error('Error:', error);
});
Метод .catch() принимает callback-функцию, вызываемую при ошибке в цепочке Promise.
Метод .finally()
Метод .finally() выполняется всегда, независимо от результата выполнения Promise:
p.then((data) => {
// ...
}).catch((error) => {
console.error('Error:', error);
}).finally(() => {
console.log('finally');
});
Утилита для задержки sleep()
Функция sleep(), создающая Promise с задержкой:
const sleep = (ms) => {
return new Promise(resolve => setTimeout(resolve, ms));
};
Использование:
sleep(2000).then(() => console.log('2 seconds'));
sleep(3000).then(() => console.log('3 seconds'));
Promise.all() и Promise.race()
Promise.all() ожидает выполнения всех переданных Promise:
Promise.all([sleep(2000), sleep(3000)]).then(() => console.log('all promises resolved'));
Promise.race() возвращает результат первого выполненного Promise:
Promise.race([sleep(2000), sleep(3000)]).then(() => console.log('first promise resolved'));
Заключение
Promise обеспечивает удобный и элегантный способ работы с асинхронными операциями в JavaScript, избегая проблемы callback hell и повышая читаемость кода. Использование цепочек Promise, методов .catch() и .finally(), а также Promise.all() и Promise.race() позволяет создавать эффективный и надежный асинхронный код.