Перевод статьи JavaScript Promises – All You Need to Know to Get Started.

В начале статьи для того, что избежать неопределенности при использовании терминологии, а точнее термина обязательство или обещание, следует обратить внимание читателей на неоднозначность точного перевода с английского языка слова promise. Поэтому я не могу не привести комментарий Коваленко В.А. переводчика книги Этана Брауна «Изучаем JavaScript. Руководство по созданию современных веб-сайтов.»

В русскоязычной документации MDN термин promise переведен как обещание. Однако по смыслу, который вложили в этот термин разработчики языка, это именно обязательство! Имеется в виду, что создавая обязательство и возвращая его в исходный код, интерпретатор JavaScript обя­зуется в дальнейшем при наступлении нужного события либо выполнить его, либо отклонить. Причем только однократно! Русскоязычный же термин обещание несет на себе некий оттенок не­ обязательности, который плохо сочетается со строгими рамками работы языка программирования. Поэтому в дальнейшем в книге мы будем использовать термин обязательство. К тому же это одно из значений английского слова promise. Переводчики документации MDN почему то решили взять его самое первое значение.

Поэтому именно термин обязательство я буду использовать для изложения материалов в этой статье.

Обязательства (обещания) Promise – одно из недавно введенных в JavaScript средств языка, которое предоставляет более простой, интуитивно понятный способ работы с асинхронным кодом. Это руководство поможет вам разобраться, что такое обязательства Promise, как их создавать, и как использовать вместе с функциями-обработчиками. В конце вы также узнаете, как работать с несколькими обязательствами в вашем коде.

Вступление

Что такое обязательство Promise в JavaScript? Обязательства Promise – это объекты, которые в конечном счете предоставляют (возвращают) некоторое значение. Когда вы создаете новое обязательство вы заранее не знаете какое именно значение вы можете получить: это могут быть данные из внешнего API, или сообщение об ошибке. Отметим, основную особенность использования обязательств, как специфичного средства языка: вы получите ожидаемое значение в некотором будущем, в случае если созданное вами обязательство Promise будет выполнено или отклонено. При следующий за обязательством promise этом код не блокируется им, не ожидает завершения его работы. Эта возможность значительно упрощает написание асинхронных приложений и делает работу и разработку более контролируемыми.

Представьте, что вами используется приложение, которому при запуске необходимо получить данные из некоторого внешнего API. Проблема в том, что вы не знаете точно, когда именно получите эти данные. Иногда их можно получить достаточно быстро. Но чаще этот процесс может занять некоторое неопределенное время. Как написать приложение, чтобы учесть эту неопределенность?

Одним из возможных вариантов решения этой задачи – проверка готовности данных в определенные промежутки времени. Приложение будет делать это пока, наконец, не получит нужные данные. Этот подход не является чистым, с точки зрения программирования, и тем более эффективным. Другой вариант – использовать такое средство языка Javascript как обязательства Promise. Вы можете использовать обязательства Promise для обращения к внешнему API. И когда это обязательство завершено, то есть разрешено (удовлетворено) или отклонено, то сможете обновить состояние своего приложения.

Как пояснить это проще? Когда обещание Promise завершено settled, то оно может быть исполнено fulfilled либо отклонено rejected, при этом интерпретатор Javascript автоматически запускает события, которые вы можете прослушивать с помощью специальных методов (функций-обработчиков). При этом вам не нужно регулярно проверять статус данных, как в первом решении.

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

Создание обязательства Promise

Это была теория, а теперь мы перейдем к практике. Для создания объекта Promise используется конструктор Promise(). С учетом синтаксиса языка Javascript его использование подразумевает также наличие ключевого слова new, за которым непосредственно помещается выражение конструктора: Promise(). Конструктор принимает только один параметр – функцию, называемую исполнителем executor. Функция-исполнитель вызывается автоматически при создании и инициализации объекта обязательства Promise.

В свою очередь функция-исполнитель принимает два параметра: resolve и reject, оба являются функциями обратного вызова. Когда обязательство разрешено – вызывается функция обратного вызова resolve. Это состояние соответствует тому, что код помещенный в Promise, был успешно выполнен (без ошибок). В случае если возникает какая-либо ошибка, и Promise отклоняется, то вызывается функция обратного вызова reject.

// Пример синтаксиса Promise
const myPromise = new Promise(function(resolve, reject) {
  // ... ваш код
})


// Пример синтаксиса Promise с использованием стрелочной функции
const myPromise = new Promise((resolve, reject) => {
  // ... ваш код
})

Исполнение и отклонение обязательств promise с использованием статических методов

Если вы хотите, чтобы обязательство Promise возвращало какие-либо данные, то вы должны передавать их в качестве аргументов при вызове функции resolve. Например, если код в обязательстве обращается к внешнему API, то вы, соответственно, передаете в функцию resolve возвращаемые этим API данные. А в случае возникновения ошибки вы передаете информацию об ошибке или сообщение об ошибке в функцию обратного вызова reject.

// Пример синтаксиса Promise
const myPromise = new Promise(function(resolve, reject) {
  // Удовлетворенный Promise получает сообщение в качестве данных
  resolve('Success: promise resolved.')

  // Если случилась ошибка
  if (error) {
    // Отклоненный Promise получает сообщение в качестве данных
    reject('Failure: promise rejected')
  }
})

// инициализируем Promise
myPromise();


// Пример синтаксиса Promise с использованием стрелочной функции
const myPromise = new Promise((resolve, reject) => {
  // Удовлетворенный Promise получает сообщение в качестве данных
  resolve('Success: promise resolved.')

  // Если случилась ошибка
  if (error) {
    // Отклоненный Promise получает сообщение в качестве данных
    reject('Failure: promise rejected')
  }
})

// инициализируем Promise
myPromise();

Если вы выполните полученный код, в консоли, то объект Promise сможет передать полученные данные функциям-обработчикам, которые вы подключили к обязательству myPromise. Есть еще две вещи о которых вы должны знать при создании своих обязательств Promise. Во-первых, вы можете передать ссылку на объект Promise переменной, а затем вызвать ее. Это похоже на использование функционального выражения.

// Создаем Promise #1: передаем ссылку на него переменной
const myPromise = new Promise((resolve, reject) => {
  // Удовлетворенный Promise получает сообщение в качестве данных
  resolve('Success: promise resolved.')

  // Если случилась ошибка
  if (error) {
    // Отклоненный Promise получает сообщение в качестве данных
    reject('Failure: promise rejected')
  }
})

// инициализируем Promise
myPromise();

Рассмотрим еще один вариант – возвращаем объект обязательства Promise из функции. Далее когда вы захотите использовать его в своем коде, то вызываете соответствующую функцию, которая инициализирует и возвращает объект Promise. При этом ваш код будет работать точно так же, как в примере выше.

Если вы хотите вернуть объект Promise, то вам необходимо использовать ключевое слово new перед конструктором Promise.

// Создаем Promise #2: возвращаем его из функции
function myFunc() {
  // Возвращаем новый Promise
  return new Promise(function(resolve, reject) {
    // Удовлетворенный Promise получает сообщение в качестве данных
    resolve('Success: promise resolved.')

    // Если случилась ошибка
    if (error) {
      // Отклоненный Promise получает сообщение в качестве данных
      reject('Failure: promise rejected')
    }
  })
}

// Вызываем функцию myFunc() для инициализации Promise внутри нее
myFunc()

Четыре состояния обязательств Promises

И так, мы знаем, что обязательства Promises или удовлетворяются (разрешаются) resolve или отклоняются rejected. По сути смысла этих терминов уже понятно, что они непосредственно связаны с состояниями, в которых может находится обязательство Promises, а точнее его объект (экземпляр). В свою очередь эти состояния определяют будет ли вызвана соответствующая функция-обработчик при переходе обязательства соответствующее состояние.

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

  • Первое состояние – ожидание решения pending. Это начальное состояние, когда вы создаете обязательство, а точнее экземпляр объекта Promises и инициализируете его. Это состояние соответствует тому, что обещание пока не удовлетворено (разрешено) resolve, но и не отклонено rejected.
  • Второе состояние – удовлетворено fulfilled, то есть обязательство было разрешено resolve и его код успешно выполнен.
  • Третье состояние – отклонено rejected. Это означает, что возникла некоторая проблема, которая помешала успешному выполнению кода обязательства.
  • Четвертое состояние – урегулировано settled. Это означает, что работа обязательства в общем завершена (его код полностью закончил свою работу) и оно в последствии было удовлетворено либо отклонено.

Обработка состояний обязательств Promises с помощью специальных функций-обработчиков

Теперь мы знаем как создать объект Promise, а также о четырех его возможных состояниях. Необходимо разобраться как работать с данными, возвращаемыми объектом Promise (результатом его работы), а точнее данными, передаваемыми в функции обратного вызова resolve и reject. И так в игру вступают методы объекта обязательства then(), catch() и finally() . Эти методы являются по сути функциями-обработчиками, которые вы можете присоединить к конкретному экземпляру объекта обязательства для прослушивания его событий.

И так вы создали и инициализировали новый объект Promise, после чего соответствующее обязательство в конечном счете будет урегулировано settled, то есть удовлетворено или отклонено. Поэтому в любом случае будет автоматически вызван один из перечисленных выше обработчиков. То есть когда из объекта Promise возвращаются данные, они будут переданы соответствующим обработчикам в зависимости от состояния, в котором будет находится объект (экземпляр) обязательства.

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

Но что делать, если вы не используете ни один из этих обработчиков? Код обязательства Promise все равно будет выполняться после того, как вы его вызовете. И данные (результат работы кода обязательства) не будут обработаны, то есть будут заблокированы внутри объекта Promise. Вот зачем необходимы функции-обработчики. Они похожи на мессенджеры, которые передают сообщения (данные) от объекта Promise далее по цепочке.

Функция-обработчик then()

Начнем с функции-обработчика then(), которая обычно используется для обработки наступления fulfilled состояния. Для того, чтобы использовать then() или вообще любой другой обработчик событий обязательства, вы должны присоединить его к объекту Promise при его инициализации (вызове). Следующий ниже пример синтаксиса соответствует передаче ссылке на объект обязательства по имени, а затем прикреплении к нему функции-обработчика .then().

// Создаем обещание Promise
const myPromise = new Promise((resolve, reject) => {
  // Имитируем задержку 
  setTimeout(function() {
    // Удовлетворенный Promise получает сообщение в качестве данных
    resolve('Promise has been resolved!')
  }, 1000)
})

// инициализируем (вызываем) myPromise и присоединяем к нему обработчик then()
// передаем функцию обратного вызова в обработчик then()
// передаем в функцию обратного вызова один параметр
myPromise.then((receivedData) => {
  // выводим в консоли данные полученные с помощью Promise
  console.log(receivedData)
})

// В консоли получим:
// 'Promise has been resolved!'

Когда обязательство будет выполнено, не важно удовлетворено или отклонено, данные (результат работы кода) будут переданы в обработчик then() и могут быть далее обработаны по вашему усмотрению. В примере выше эти данные передаются через функцию обратного вызова resolve(), которая вызывается в коде обязательства. Таким образом, если вы хотите получить доступ к данным, результату работы кода обязательства, вам нужно передать их в функцию обратного вызова, и далее они будут доступны в обработчике then().

Функция обратного вызова resolve() принимает только один параметр (возвращаемые данные). И таким образом любые данные, возвращаемые обязательством после его удовлетворения resolve, будут доступны в функции-обработчике.

У обработчика then() есть одна интересная особенность – вы можете использовать его для обработки возвращаемых из кода обязательства данных для состояния rejected (отклонено). И как же нам обеспечить обработку обоих состояний?

Дело в том, что первый параметр, передаваемый в функцию-обработчик, должен быть функций обратного вызова, которая будет использоваться для обработки состояния fulfilled. А если вы хотите, чтобы было обработано состояние, при котором обязательство отклонено rejected, то вам нужно просто передать в в then() вторую функцию обратного вызова.

Вторая функция обратного вызова, по аналогии, тоже принимает только один параметр. Используя его вы можете получить доступ к любой информации об ошибках, переданной из объекта обязательства Promise. Тем не менее обработчик then() в основном используется для обработки состояния fulfilled, и реже для rejected. Так как передать функцию обратного вызова для обработки отклоненного состояния rejected «трудно» без передачи первой, ответственной за состояние fulfilled.

// Создаем Promise
const myPromise = new Promise((resolve, reject) => {
  // Имитируем задержку выполнения кода
  setTimeout(function() {
    // Отклоненный Promise получает сообщение в качестве данных
    reject('Promise has been rejected...')
  }, 1000)
})

// инициализируем myPromise и присоединяем к нему обработчик then()
// передаем функцию обратного вызова в обработчик then()
// передаем в функцию обратного вызова один параметр
myPromise.then((receivedData) => {
  // Это первая функция обратного вызова для состояния 'fulfilled'
  // выводим в консоли данные полученные с помощью Promise
  console.log(receivedData)
}, (error) => { // <= Remember to separate handlers with comma
  // Это вторая функция обратного вызова для состояния 'rejected'
  console.log(error)
})

// В консоли получим:
// 'Promise has been rejected...'

Примечание. Не забудьте разделить функции обратного вызова для обработки состояний удовлетворения и отклонения обязательства запятой, если вы все еже решите использовать функцию-обработчика then() для обработки обоих состояний.

Функция-обработчик catch()

Далее рассмотрим метод catch(), который используется для обработки состояния reject, то есть обязательство было отклонено. Использование этого обработчика, как и доступ к любым данным, переданным в него, аналогично обработчику then(). Если вы хотите использовать его и присоединить к Promise, то рекомендуется прикреплять его после обработчика then(). Когда вы присоединяете его, вы должны передать ему функцию обратного вызова, которая принимает также только один параметр. Когда обещание Promise отклоняется, любые данные, переданные в обработчик reject() внутри объявления Promise, будут доступны через ее единственный параметр.

// Пример с Reject #1: без then()
// Создаем Promise
const myPromise = new Promise((resolve, reject) => {
  // Имитируем задержку выполнения кода
  setTimeout(function() {
    // Отклоненный Promise получает сообщение в качестве данных
    reject('Promise has been rejected...')
  }, 1000)
})
// инициализируем myPromise и присоединяем к нему обработчик catch()
// передаем функцию обратного вызова в обработчик catch()
// передаем в функцию обратного вызова один параметр
myPromise.catch((error) => {
  // выводим в консоли данные полученные с помощью Promise
  console.log(error)
})

// В консоли получим:
// 'Promise has been rejected...'


// Пример с Reject #1: с then()
// Создаем Promise
const myPromise = new Promise((resolve, reject) => {
  // Имитируем задержку выполнения кодаy
  if (error) {
    // Удовлетворенный Promise получает сообщение в качестве данных
    reject('Promise has been rejected...')
  } else {
    resolve('Promise has been resolved.')
  }
})

// инициализируем myPromise и сразу присоединяем обработчик then()
// передаем в функцию обратного вызова один параметр
// далее присоединяем обработчик catch(), и также передаем 
// в него функцию обратного вызова с одним параметром
myPromise
  .then((receivedData) => {
    // выводим в консоли данные полученные с помощью Promise
    console.log(receivedData)
  })
  .catch((error) => {
    // выводим в консоли сообщение об ошибке, полученное из Promise
    console.log(error)
  })

Функция-обработчик finally()

Метод finally() это последняя функция-обработчик, которую мы рассмотрим. Особенностью этого обработчика является то, что он будет вызываться каждый раз при переходе Promise в состояние settled. При этом он будет вызван независимо от того, как закончил работу код Promise, то есть с переходом в состояние fulfilled или rejected. Использование этого обработчика может быть полезно, если вы хотите что-то сделать независимо от конечного состояния обязательства.

Обработчик finally() используется так же, как и другие функции-обработчики then() и catch(). Вы прикрепляете его к объекту Promise, когда вызываете его. finally() обычно прикрепляется в качестве самого последнего обработчика, после then() и catch(). В отличие от двух предыдущих, этот обработчик не требует никаких параметров, так как в него ничего не передается.

const myPromise = new Promise((resolve, reject) => {
  // Имитируем задержку выполнения кода
  setTimeout(function() {
    // Удовлетворенный Promise получает сообщение в качестве данных
    resolve('Promise has been resolved.')
  }, 1000)
})

// инициализируем myPromise и сразу присоединяем обработчик then()
// далее присоединяем обработчик catch()
// далее присоединяем обработчик finally()
myPromise
  .then((receivedData) => {
    // выводим в консоли данные полученные с помощью Promise
    console.log(receivedData)
  })
  .catch((error) => {
    // выводим в консоли сообщение об ошибке, полученное из Promise
    console.log(error)
  })
  .finally(() => {
    // выводим в консоли информационное сообщение
    console.log('Promise is done.')
  })

// В консоли получим:
// 'Promise has been resolved.'
// 'Promise is done.'

Методы объекта Promise

Работать с обязательствами в JavaScript достаточно просто когда в вашем коде их используется всего одно или два. Но что, если вам необходимо обрабатывать сразу несколько обязательств? К счастью JavaScript предоставляет несколько методов, которые упрощают эту задачу. Это такие методы как all(), allSettled(), race() и any().

Все эти методы в качестве параметров принимают итерируемый объект, например массив. Этот объект содержит обязательства , которые вы хотите вызвать и обработать данные, полученные из них. Разница в том, что каждый из этих методов работает по-своему и приводит к получению разных результатов. Итак, давайте разберемся с каждым из них.

Promise.all()

Когда вы передадите в метод Promise.all() обязательства, то он попытается выполнить их по очереди и разрешить их все. В случае если все переданные обязательства будут удовлетворены (разрешены) то метод Promise.all() вернет одно обязательство, содержащее все полученные значения. Получить доступ к этому значению, можно присоединив обработчик then() к методу Promise.all(), передав ему соответствующую функцию обратного вызова.

Если происходит так, что одно или несколько из обязательств переданных в метод Promise.all() отклоняются, то сразу возвращается значение из первого отклоненного обязательства. Это важно помнить. И так если одно из обязательство «терпит неудачу» то Promise.all(), вернет только значение переданное из первого отклоненного обязательства. То есть метод не будет возвращать данные из любых до этого удовлетворённых (разрешенных) обязательств.

// Пример #2: все обязательства Promises разрешены
// создаем первое обязательство Promise которое будет разрешено
const myPromiseOne = new Promise((resolve, reject) => {
  // имитируем задержку в выполнении кода
  setTimeout(function() {
    // Удовлетворенный Promise получает сообщение в качестве данных
    resolve('myPromiseOne has been resolved.')
  }, 500)
})

// создаем второе обязательство Promise которое будет разрешено
const myPromiseTwo = new Promise((resolve, reject) => {
  // имитируем задержку в выполнении кода
  setTimeout(function() {
    // Удовлетворенный Promise получает сообщение в качестве данных
    resolve('myPromiseTwo has been resolved.')
  }, 1000)
})

// Используем Promise.all() для обработки всех обязательств Promises
Promise.all([myPromiseOne, myPromiseTwo])
  .then((data) => {
    // выводим в консоли данные полученные для случая когда Promise разрешен
    console.log(data)
  })
  .catch((error) => {
    // Выводим в консоли сообщение об ошибке если Promise отклонен
    console.log(error)
  })

// Выведет в консоли:
// [
//   'myPromiseOne has been resolved.',
//   'myPromiseTwo has been resolved.'
// ]


// Пример #2: средний Promise будет отклонен
// создаем первое обязательство Promise которое будет разрешено
const myPromiseOne = new Promise((resolve, reject) => {
  // имитируем задержку в выполнении кода
  setTimeout(function() {
    // Удовлетворенный Promise получает сообщение в качестве данных
    resolve('myPromiseOne has been resolved.')
  }, 500)
})

// создаем второе обязательство Promise, которое будет отклонено
const myPromiseTwo = new Promise((resolve, reject) => {
  // имитируем задержку в выполнении кода
  setTimeout(function() {
    // Отклоненный Promise получает сообщение в качестве данных
    reject('myPromiseTwo has been rejected.')
  }, 1000)
})

// создаем третье обязательство Promise, которое будет разрешено
const myPromiseThree = new Promise((resolve, reject) => {
  // имитируем задержку в выполнении кода
  setTimeout(function() {
    // Удовлетворенный Promise получает сообщение в качестве данных
    resolve('myPromiseThree has been resolved.')
  }, 1500)
})

// Используем Promise.all() для обработки всех обязательств Promises
Promise.all([myPromiseOne, myPromiseTwo, myPromiseThree])
  .then((data) => {
    // выводим в консоли данные полученные для случая когда Promise разрешен
    console.log(data)
  })
  .catch((error) => {
    // Выводим в консоли сообщение об ошибке если Promise отклонен
    console.log(error)
  })

// Выведет в консоли:
// 'Error: myPromiseTwo has been rejected'

// !! Notice that the data from myPromiseOne that was resolved
// before the myPromiseTwo was rejected are missing

Примечание. Не забудьте добавить обработчик catch() при использовании метода Promise.all(). Или добавьте второй обратный вызов в метод then(). В противном случае вы не получите никаких данных об ошибках, если одно из обещаний будет отклонено.

Promise.allSettled()

Рассмотрим еще один очень полезный метод Promise.allSettled(), который можно использовать для обработки нескольких обязательств. Отметим, что метод Promise.allSettled() работает аналогично Promise.all(). Он также попытается разрешить все переданные в него обязательства.

Разница в том, что если одно из обязательств отклоняется, то Promise.allSettled() все равно ожидает выполнение кода других обязательств. Таким образом, когда работа всех обязательств завершена, то есть они находятся в состоянии settled, только тогда метод Promise.allSettled() вернет все значения, полученные от переданных в него обязательств. То есть вернет все значения, независимо от того, отклонено rejected или удовлетворено resolve какое-либо из этих обещаний. Это его самое главное отличие от метода Promise.all().

Значения, возвращаемые методом, Promise.allSettled() имеют вид объектов. Каждый объект кроме возвращаемых данных содержит статус (состояние), с которым код обязательства закончил работу, то есть fulfilled или rejected. Если обязательство разрешено, то соответствующий объект будет содержать поле value – значение, полученное из него. Если обязательство отклонено, то соответствующий объект содержит поле reason – данные об ошибке.

Это делает его использование в большинстве случаев лучшим выбором, чем метод Promise.all(). Кроме того вам не нужно беспокоиться о потере значений, получаемых от всех удовлетворенных (разрешенных) обязательств только из-за сбоя в работе одного из них. Вместо этого вы получите все значения как от тех обязательств, которые были разрешены resolve, так и от тех, которые были отклонены rejected.

// создаем первое обязательство Promise которое будет разрешено
const myPromiseOne = new Promise((resolve, reject) => {
  // имитируем задержку в выполнении кода
  setTimeout(function() {
    // Удовлетворенный Promise получает сообщение в качестве данных
    resolve('myPromiseOne has been resolved.')
  }, 500)
})

// создаем второе обязательство Promise которое будет отклонено
const myPromiseTwo = new Promise((resolve, reject) => {
  // имитируем задержку в выполнении кода
  setTimeout(function() {
    // Отклоненный Promise получает сообщение в качестве данных
    reject('myPromiseTwo has been rejected!')
  }, 1000)
})

// создаем третье обязательство Promise которое будет разрешено
const myPromiseThree = new Promise((resolve, reject) => {
  // имитируем задержку в выполнении кода
  setTimeout(function() {
    // Удовлетворенный Promise получает сообщение в качестве данных
    resolve('myPromiseThree has been resolved.')
  }, 1500)
})

// Используем Promise.allSettled() для обработки всех обязательств Promises 
Promise.allSettled([myPromiseOne, myPromiseTwo, myPromiseThree])
  .then((data) => {
    // выводим в консоли данные полученные от разрешенных обязательств
    console.log(data)
  })

// Выведет в консоли:
// [
//   {
//     status: 'fulfilled',
//     value: 'myPromiseOne has been resolved.'
//   },
//   {
//     status: 'rejected',
//     reason: 'myPromiseTwo has been rejected!' },
//   {
//     status: 'fulfilled',
//     value: 'myPromiseThree has been resolved.'
//   }
// ]

Promise.race()

Метод Promise.race() делает именно то, что предписывает его название (race – гонка). Для работы этого метода требуется как минимум пара обязательств, которые будут «участвовать в гонке». Это означает, что метод в качестве результата вернет новое обязательство, как только одно из переданных ему будет удовлетворено или отклонено. Это новое обязательство будет содержать либо значение в поле value, то есть какие либо данные, либо сообщение об ошибке в поле reason. Возвращаемое значение будет определяться тем, какое из переданных в метод обязательств будет выполнено быстрее всех, при этом не важно, удовлетворено оно будет либо отклонено.

// создаем первое обязательство Promise которое будет разрешено
const myPromiseOne = new Promise((resolve, reject) => {
  // имитируем задержку в выполнении кода
  setTimeout(function() {
    // Удовлетворенный Promise получает сообщение в качестве данных
    resolve('myPromiseOne has been resolved.')
  }, 500)
})

// создаем второе обязательство Promise которое будет разрешено
const myPromiseTwo = new Promise((resolve, reject) => {
  // имитируем задержку в выполнении кода
  setTimeout(function() {
    // Удовлетворенный Promise получает сообщение в качестве данных
    resolve('myPromiseTwo has been rejected!')
  }, 1000)
})

// создаем третье обязательство Promise которое будет разрешено
const myPromiseThree = new Promise((resolve, reject) => {
  // имитируем задержку в выполнении кода
  setTimeout(function() {
    // Удовлетворенный Promise получает сообщение в качестве данных
    resolve('myPromiseThree has been resolved.')
  }, 1500)
})

// Используем Promise.race() для обработки всех обещаний Promises
Promise.race([myPromiseOne, myPromiseTwo, myPromiseThree])
  .then((data) => {
    // выводим в консоли данные полученные от разрешенных обязательств
    console.log(data)
  })
  .catch((error) => {
    // выводим в консоли сообщения полученные от отклоненных обязательств
    console.log(error)
  })

// В консоли выведет:
// 'myPromiseOne has been resolved.'

Примечание: аналогично Promise.all(), к методу Promise.race() возможно подключение обработчика catch(). Или по аналогии добавить вторую функцию обратного вызова в метод then(). В противном случае вы не получите никаких сообщений об ошибке, если первое переданное для обработки в метод обязательство будет отклонено.

Promise.any()

Метод Promise.any() по сути аналогичен Promise.race(). Разница между ними в том, что Promise.any() игнорируют любые полностью выполненные, но отклоненные обязательства. Он вернет значение только первого выполненного и удовлетворенного fulfilled обязательства.

// создаем первое обязательство Promise которое будет отклонено
const myPromiseOne = new Promise((resolve, reject) => {
  // имитируем задержку в выполнении кода
  setTimeout(function() {
    // Отклоненный Promise получает сообщение в качестве данных
    reject('myPromiseOne has been resolved.')
  }, 500)
})

// создаем второе обязательство Promise которое будет отклонено
const myPromiseTwo = new Promise((resolve, reject) => {
  // имитируем задержку в выполнении кода
  setTimeout(function() {
    // Отклоненный Promise получает сообщение в качестве данных
    reject('myPromiseTwo has been rejected!')
  }, 1000)
})

// создаем первое обязательство Promise которое будет разрешено
const myPromiseThree = new Promise((resolve, reject) => {
  // имитируем задержку в выполнении кода
  setTimeout(function() {
    // Удовлетворенный Promise получает сообщение в качестве данных
    resolve('myPromiseThree has been resolved.')
  }, 1500)
})

// Используем Promise.all() для обработки всех обязательств Promises
Promise.any([myPromiseOne, myPromiseTwo, myPromiseThree])
  .then((data) => {
    // выводим в консоли данные полученные от разрешенных обязательств
    console.log(data)
  })
  .catch((error) => {
    // Выводим в консоли сообщения полученные от отклоненных обязательств
    console.log(error)
  })

// Выведет в консоли:
// 'myPromiseThree has been resolved.'

Примечание. На момент написания этой статьи Promise.any() находится на Stage 3 предложения. Это означает, что это нестабильная часть языка JavaScript и метод все еще является экспериментальным и поддерживается не всеми браузерами.

Заключение: обязательства JavaScript

Поздравляю! Вы только что закончили читать статью об обязательствах Promise в JavaScript. Если вы внимательно ознакомились с содержанием этой статьи, то теперь знаете, что такое обязательства, как их создавать, по какому принципу он удовлетворяются или отклоняются. Вы узнали основы использования функций-обработчиков then(), catch()и finally(), предназначенных для обработки данных, возвращаемых выполненными обязательствами Promises.

И, наконец, вы теперь в состоянии использовать другие обязательств такие как Promise.all(), Promise.allSettled(), Promise.race()и Promise.any(), которые используются для работы с несколькими обязательствами одновременно. Теперь вы хорошо подготовлены для написания более чистого и интуитивно понятного асинхронного кода JavaScript. А также можете, наконец, попрощаться с адом вложенных обратных вызовов.

1 комментарий

Оставить комментарий