В спецификации 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()
в конце цепочки, даже если уверены в результате. Это позволит вам избежать непредвиденных проблем в дальнейшем.
Несколько дополнительных правил, которые нужно освоить сразу:
После срабатывания .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) // 42
Promise не является панацеей, но позволяет более структурно организовать ваш код. В современных приложениях он становится де-факто главным механизмом организации работы с асинхронными данными.
Оцените удобство на практике!