Знакомимся с Promise

В спецификации ES2015 отведено место под такую синтаксическую конструкцию, как Promise (дословно на русский - Обещание). Бывалые уже встречались со таким понятием в jQuery. В этой библиотеке есть реализация, которая делает схожие вещи, но отличается по синтаксису и функционалу. В этой статье мы будем рассматривать именно реализацию по стандарту Promises/A+.
Говоря простым языком, Promise - это обертка, которая содержит свое состояние. Состоянием при создании данной обертки является pending, которое, в зависимости от результата выполнения, меняет свое состояние на resolved (получен результат) и rejected (произошла ошибка).
Для того, чтобы понять, в каких ситуациях могут использоваться Promise, достаточно привести два примера:
- При необходимости получить результат асинхронного запроса и, в момент окончания выполнения операции, продолжить исполнение кода.
- При необходимости выполнить некоторый код после определенного тайм-аута.
Конечно, в обоих случаях для этого уже существуют обратные вызовы (т.н. callbacks), но Promise здесь выступает как более удобная альтернатива, и вот почему.
Новая спецификация предлагает записывать такие вызовы в виде звеньев цепочки (chain), причём следующее звено вызывается только в случае полного выполнения предыдущего.
Насколько это удобно? Давайте посмотрим.
Подробнее о синтаксисе Promise можно почитать на Mozilla Developer Network.
Ниже приведена стандартная минимально необходимая цепочка:
new Promise((resolve, reject) => setTimeout(resolve, 2000))
.then(() => console.log('success!'))
.catch(() => console.log('fail :('))Обратите внимание на её синтаксис. Для того, чтобы обозначить момент выполнения асинхронного действия, создается новый Promise. Затем, при успешном его выполнении, используется метод .then() для того, чтобы выполнить следующее действие. В случае возникновения непредвиденной ошибки используется метод .catch().
всегда используйте
.catch()в конце цепочки, даже если уверены в результате. Это позволит вам избежать непредвиденных проблем в дальнейшем.
Несколько дополнительных правил, которые нужно освоить сразу:
- Promise никогда не возвращает чистое значение. Результат выполнения Promise может использоваться только внутри него.
- Promise может возвращать другой Promise, создавая таким образом цепочку из нескольких последовательных асинхронных действий.
После срабатывания .catch(), Promise снова становится валидным, и вы можете продолжить выполнять действия по цепочке:
Promise.reject()
.catch(() => console.log('something went wrong...'))
.then(() => console.log('everything is good again!'))Кстати, о .resolve() и .reject(). Как видно из примера выше, они существуют не только в виде аргументов при создании нового Promise, но и как самостоятельные методы. Используется второй подход, как правило, лишь в одном случае: когда результат выполнения уже известен, и вам просто нужно вернуть либо успешный, либо неуспешный результат выполнения.
Promise.resolve(32)
.then(result => result + 10)
.then(result => console.log(result)) // 42Однако это не все методы, которые имеются в арсенале Promise. Одним из удобных инструментов являются методы, позволяющие реагировать на результат нескольких параллельных асинхронных запросов. Вот и они:
Promise.all- ожидает, пока все асинхронные действия будут выполненыPromise.race- ожидает перого выполненного асинхронного действия
Пользоваться ими очень просто и удобно. Достаточно передать массив с Promise, и на выходе будет создан новый, содержащий все необходимые данные.
Promise.all([
Promise.resolve(32),
Promise.resolve(10)
]).then(([result1, result2] = result) => result1 + result2) // 42Promise не является панацеей, но позволяет более структурно организовать ваш код. В современных приложениях он становится де-факто главным механизмом организации работы с асинхронными данными.
Оцените удобство на практике!