Promise: примеры (JAVASCRIPT)
Promise(executor (function)): PromiseОсновы Promise в JavaScript
Объект Promise (Обещание) используется для отложенных и асинхронных вычислений. Он представляет собой значение, которое может быть доступно сейчас, в будущем или никогда. Promise находится в одном из трех состояний: ожидание (pending), выполнено (fulfilled), отклонено (rejected).
Использование Promise характерно для операций, требующих времени: сетевые запросы, чтение файлов, таймеры. Конструктор принимает функцию-исполнитель (executor) с двумя параметрами: resolve и reject. Функция-исполнитель выполняется немедленно при создании промиса.
Аргументы конструктора:
- executor: Функция
(resolve, reject) => {}. Вызывается конструктором Promise. Код внутри этой функции инициирует асинхронную операцию. - resolve: Функция, вызываемая при успешном завершении. Передает результат операции.
- reject: Функция, вызываемая при ошибке. Передает объект ошибки.
Возвращаемое значение: Конструктор возвращает новый объект Promise. Этот объект имеет методы .then(), .catch() и .finally() для обработки результатов.
Простые примеры использования Promise
Пример создания и обработки успешного обещания.
const successPromise = new Promise((resolve, reject) => {
setTimeout(() => resolve('Данные получены'), 1000);
});
successPromise.then(result => console.log(result));// Через 1 секунду:
Данные полученыПример с обработкой ошибки.
const errorPromise = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error('Ошибка загрузки')), 1000);
});
errorPromise
.then(result => console.log(result))
.catch(error => console.error(error.message));// Через 1 секунду:
Ошибка загрузкиИспользование метода Promise.resolve() для немедленного создания выполненного промиса.
const immediatePromise = Promise.resolve('Мгновенное значение');
immediatePromise.then(value => console.log(value));Мгновенное значениеАльтернативные подходы в JavaScript
Async/await: Синтаксический сахар над промисами для написания асинхронного кода в синхронном стиле. Предпочтителен для последовательных операций и улучшения читаемости.
async function fetchData() {
try {
const response = await fetch('api/data');
const data = await response.json();
console.log(data);
} catch (error) {
console.error(error);
}
}Callback-функции: Традиционный подход, где функция передается как аргумент и вызывается по завершению операции. Приводит к "аду колбэков" при сложной вложенности. Промисы решают эту проблему.
Event Emitters / Observables (RxJS): Используются для реактивного программирования и работы с потоками событий. Подходят для сценариев с множественными повторяющимися событиями.
Концепции в других языках программирования
Python (asyncio): Использует ключевые слова async и await для корутин. Объекты Future аналогичны Promise.
import asyncio
async def main():
await asyncio.sleep(1)
print('Готово')
asyncio.run(main())# Через 1 секунду:
ГотовоPHP (Promises): Реализации через библиотеки, например Guzzle. С версии 8.1 появились Fibers для асинхронности.
// Пример с библиотекой Guzzle
$promise = $httpClient->requestAsync('GET', 'url');
$promise->then(
function ($response) { echo 'Успех'; },
function ($reason) { echo 'Ошибка'; }
);C# (Task): Тип Task и Task<T> с ключевыми словами async/await. Более тесно интегрированы в среду выполнения .NET.
public async Task<string> GetDataAsync()
{
await Task.Delay(1000);
return "Данные";
}Распространенные ошибки разработчиков
1. Потеря обработки ошибок: Отсутствие блока .catch() приводит к тихому провалу.
const riskyPromise = new Promise((resolve, reject) => {
reject(new Error('Проблема'));
});
// Ошибка не будет обработана, возможно предупреждение в консоли.2. Вложенность промисов (Pyramid of Doom): Вместо цепочки методов создается сложная структура.
// Плохо
getUser().then(user => {
getProfile(user).then(profile => {
// ...
});
});
// Хорошо
getUser()
.then(user => getProfile(user))
.then(profile => { /* ... */ });3. Забытый return в цепочке then: Приводит к тому, что следующий then получает undefined.
Promise.resolve(5)
.then(value => {
value * 2; // Здесь нет return
})
.then(result => console.log(result)); // Выведет undefinedЭволюция Promise в современных версиях
Спецификация ES2020 добавила метод Promise.allSettled(). Он ждет завершения всех промисов, независимо от их статуса, и возвращает массив результатов с информацией о состоянии.
const promises = [
Promise.resolve('Успех'),
Promise.reject('Отказ'),
Promise.resolve('Еще один успех')
];
Promise.allSettled(promises).then(results => {
results.forEach(result => console.log(result.status, result.value || result.reason));
});fulfilled Успех
rejected Отказ
fulfilled Еще один успехТакже ES2021 представил метод Promise.any(), который ожидает выполнения первого успешного промиса, игнорируя отклоненные. Если все промисы отклонены, возвращается AggregateError.
Сложные и специальные примеры
Имитация запросов с задержкой и использование Promise.all() для параллельного выполнения.
const fetchUser = () => new Promise(resolve => setTimeout(() => resolve({id: 1, name: 'Иван'}), 800));
const fetchOrders = () => new Promise(resolve => setTimeout(() => resolve([1, 2, 3]), 500));
Promise.all([fetchUser(), fetchOrders()])
.then(([user, orders]) => {
console.log('Пользователь:', user.name);
console.log('Заказы:', orders.length);
})
.catch(error => console.error('Ошибка в одном из запросов:', error));// После ~800 мс (максимальной задержки):
Пользователь: Иван
Заказы: 3Использование Promise.race() для создания таймаута операции.
const dataFetch = new Promise(resolve => setTimeout(() => resolve('Данные с сервера'), 2000));
const timeout = new Promise((_, reject) => setTimeout(() => reject(new Error('Таймаут')), 1000));
Promise.race([dataFetch, timeout])
.then(data => console.log(data))
.catch(error => console.error(error.message));// Через 1 секунду:
ТаймаутСоздание промиса, который повторяет попытку операции при неудаче.
function retryOperation(operation, retries) {
return new Promise((resolve, reject) => {
const attempt = (n) => {
operation()
.then(resolve)
.catch(error => {
if (n === 0) {
reject(error);
} else {
console.log(`Попытка ${retries - n + 1} не удалась, пробую еще...`);
attempt(n - 1);
}
});
};
attempt(retries);
});
}
// Пример использования с функцией, которая иногда падает
const unstableFetch = () => new Promise((res, rej) => Math.random() > 0.3 ? res('OK') : rej('Временный сбой'));
retryOperation(unstableFetch, 3)
.then(result => console.log('Итог:', result))
.catch(finalError => console.error('Все попытки исчерпаны:', finalError));