Промис¶
Промис (от англ. promise — обещание) — это объект, используемый в асинхронном программировании, который представляет собой результат успешного или неудачного завершения асинхронной операции. Промисы являются одним из ключевых механизмов для работы с асинхронным кодом, позволяя избежать так называемого «ада колбэков» (callback hell) и обеспечивая более читаемый и управляемый способ обработки последовательностей асинхронных действий.
¶История
Концепция промисов как абстракции для асинхронных вычислений возникла не в JavaScript. Впервые идея была предложена в 1976 году в контексте языков программирования, ориентированных на параллельные вычисления. В 1980-х годах подобные механизмы были реализованы в языке Scheme (формы future и promise). В 1990-х годах концепция развивалась в сообществе функционального программирования, в частности в языке Haskell, где были введены монады для обработки побочных эффектов.
В мире JavaScript первые реализации промисов появились в начале 2010-х годов. Значительную роль в популяризации сыграли библиотеки, такие как jQuery (метод .deferred()), Q (Крис Коваль) и Bluebird (Петко Валахов). В 2012 году был предложен стандарт Promises/A+, который определил единый интерфейс для работы с промисами. В 2015 году промисы были официально включены в стандарт ECMAScript 6 (ES2015) и стали встроенной частью языка JavaScript. С тех пор они являются основой для многих современных асинхронных API, включая Fetch API, а также для синтаксиса async/await, который был добавлен в стандарт ECMAScript 2017.
¶Определение и основные свойства
Промис представляет собой объект, который может находиться в одном из трёх состояний:
- Ожидание (pending): начальное состояние, операция ещё не завершена.
- Исполнен (fulfilled): операция завершена успешно, промис имеет значение (результат).
- Отклонён (rejected): операция завершена с ошибкой, промис имеет причину ошибки (reason).
После того как промис перешёл в состояние «исполнен» или «отклонён», он считается «завершённым» (settled). Изменить состояние завершённого промиса невозможно. Это свойство называется «неизменяемость» (immutability) и гарантирует, что результат асинхронной операции будет доставлен ровно один раз.
¶Устройство и API
Основной способ создания промиса — использование конструктора Promise. Конструктор принимает функцию-исполнитель (executor), которая, в свою очередь, принимает два аргумента: функции resolve и reject. Вызов resolve(value) переводит промис в состояние «исполнен» с указанным значением. Вызов reject(reason) переводит промис в состояние «отклонён» с указанной причиной ошибки.
Основные методы для работы с промисами:
Promise.prototype.then(onFulfilled, onRejected): добавляет обработчики для успешного и/или неудачного завершения промиса. Возвращает новый промис, что позволяет создавать цепочки вызовов.Promise.prototype.catch(onRejected): добавляет обработчик только для ошибки. Эквивалентен вызову.then(undefined, onRejected).Promise.prototype.finally(onFinally): добавляет обработчик, который вызывается независимо от того, завершился ли промис успешно или с ошибкой. Позволяет выполнить код очистки (например, закрыть соединение).
Статические методы класса Promise:
Promise.all(iterable): возвращает промис, который исполняется, когда все переданные промисы завершатся успешно. Если хотя бы один промис отклоняется, возвращаемый промис немедленно отклоняется с причиной ошибки первого отклонённого промиса.Promise.allSettled(iterable): возвращает промис, который исполняется, когда все переданные промисы завершатся (независимо от результата). Возвращает массив объектов с описанием результата каждого промиса.Promise.race(iterable): возвращает промис, который завершается состоянием первого завершённого промиса из переданного итерируемого объекта (как успешного, так и отклонённого).Promise.any(iterable): возвращает промис, который исполняется, когда хотя бы один из переданных промисов завершится успешно. Если все промисы отклоняются, возвращаемый промис отклоняется с ошибкойAggregateError.Promise.resolve(value): возвращает промис, который уже находится в состоянии «исполнен» с переданным значением.Promise.reject(reason): возвращает промис, который уже находится в состоянии «отклонён» с переданной причиной ошибки.
¶Применение
Промисы широко используются в веб-разработке и серверном программировании на JavaScript (Node.js) для обработки асинхронных операций:
- HTTP-запросы: работа с Fetch API, XMLHttpRequest.
- Работа с файлами: чтение и запись файлов в Node.js (модуль
fs/promises). - Работа с базами данных: выполнение запросов к базам данных (например, MongoDB, PostgreSQL).
- Таймеры и задержки: создание промисов для ожидания определённого времени (
setTimeout). - Анимации: управление последовательностью анимаций.
- Пользовательские асинхронные функции: любая функция, выполняющая асинхронную операцию, может возвращать промис.
¶Синтаксис async/await
Синтаксис async/await, введённый в ECMAScript 2017, является синтаксическим сахаром над промисами. Ключевое слово async перед объявлением функции делает её асинхронной и заставляет её возвращать промис. Ключевое слово await можно использовать только внутри async-функции. Оно приостанавливает выполнение функции до тех пор, пока переданный промис не будет завершён, и возвращает его значение. Это позволяет писать асинхронный код, который выглядит как синхронный, что значительно повышает читаемость и упрощает обработку ошибок (с помощью стандартных конструкций try...catch).
¶Примеры
¶Создание и использование промиса
```javascript const myPromise = new Promise((resolve, reject) => { setTimeout(() => { const success = true; if (success) { resolve("Операция выполнена успешно"); } else { reject(new Error("Произошла ошибка")); } }, 1000); });
myPromise .then((result) => { console.log(result); // "Операция выполнена успешно" }) .catch((error) => { console.error(error.message); }); ```
¶Цепочка промисов
``javascript fetch('https://api.example.com/data') .then(response => response.json()) .then(data => { console.log('Полученные данные:', data); return data.id; }) .then(id => fetch(https://api.example.com/details/${id})) .then(response => response.json()) .then(details => console.log('Детали:', details)) .catch(error => console.error('Ошибка в цепочке:', error)); ```
¶Использование async/await
``javascript async function fetchData() { try { const response = await fetch('https://api.example.com/data'); const data = await response.json(); console.log('Полученные данные:', data); const id = data.id; const detailsResponse = await fetch(https://api.example.com/details/${id}); const details = await detailsResponse.json(); console.log('Детали:', details); } catch (error) { console.error('Ошибка:', error); } } ```
¶Критика и ограничения
Несмотря на широкое распространение, промисы имеют ряд недостатков и критических замечаний:
- Сложность отладки: цепочки промисов могут быть сложными для отладки, особенно при большом количестве вложенных вызовов или при обработке ошибок, которые могут быть «проглочены».
- Необходимость явного возврата: при построении цепочек необходимо явно возвращать промис из обработчика
.then(), иначе следующий обработчик получитundefined. - Отсутствие отмены: нативные промисы не имеют встроенного механизма отмены операции. Это может привести к выполнению ненужных запросов или утечкам памяти. Существуют сторонние решения (например,
AbortControllerдля Fetch API), но они не являются частью спецификации промисов. - Обработка ошибок: если не добавить обработчик
.catch()в конце цепочки, ошибка может остаться незамеченной. В среде Node.js это может привести к падению процесса. - Проблемы с производительностью: создание большого количества промисов может привести к дополнительным накладным расходам памяти и процессорного времени, хотя в современных движках JavaScript эти проблемы минимизированы.
¶Источники
- ECMAScript Language Specification (ECMA-262) — раздел «Promise Objects».
- Promises/A+ Specification — официальный стандарт, описывающий поведение thenable-объектов.
- Документация MDN Web Docs (Mozilla Developer Network) — раздел «Promise».
- Книга «You Don't Know JS: Async & Performance» (Кайл Симпсон).
- Книга «JavaScript: The Good Parts» (Дуглас Крокфорд).
BFOmetr — база данных и аналитика по компаниям России.
На главную BFOmetr →

