Alxblsk.com Logo
RE•DONE
Blog by Aliaksei Belski

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

Published: September 20th, 2017, Updated: September 12th, 2020javascriptPromisesECMAScript2015

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

В спецификации ES2015 отведено место под такую синтаксическую конструкцию, как Promise (дословно на русский - Обещание). Бывалые уже встречались со таким понятием в jQuery. В этой библиотеке есть реализация, которая делает схожие вещи, но отличается по синтаксису и функционалу. В этой статье мы будем рассматривать именно реализацию по стандарту Promises/A+.

Говоря простым языком, Promise - это обертка, которая содержит свое состояние. Состоянием при создании данной обертки является pending, которое, в зависимости от результата выполнения, меняет свое состояние на resolved (получен результат) и rejected (произошла ошибка).

Для того, чтобы понять, в каких ситуациях могут использоваться Promise, достаточно привести два примера:

  1. При необходимости получить результат асинхронного запроса и, в момент окончания выполнения операции, продолжить исполнение кода.
  2. При необходимости выполнить некоторый код после определенного тайм-аута.

Конечно, в обоих случаях для этого уже существуют обратные вызовы (т.н. 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) // 42

Promise не является панацеей, но позволяет более структурно организовать ваш код. В современных приложениях он становится де-факто главным механизмом организации работы с асинхронными данными.

Оцените удобство на практике!