Promise

Сводка

Объект Promise (промис) используется для отложенных и асинхронных вычислений.

Синтаксис

new Promise(executor);
new Promise(function(resolve, reject) { ... });

Параметры

executor
Объект функции с двумя аргументами resolve и reject. Функция executor  получает оба аргумента и выполняется сразу, еще до того как конструктор вернет созданный объект. Первый аргумент (resolve) вызывает успешное исполнение промиса, второй (reject) отклоняет его.
Обычно функция executor описывает выполнение какой-то асинхронной работы, по завершении которой необходимо вызвать функцию resolve или reject. Обратите внимание, что возвращаемое значение функции executor игнорируется.

Описание

Интерфейс Promise (промис) представляет собой обертку для значения, неизвестного на момент создания промиса. Он позволяет обрабатывать результаты асинхронных операций так, как если бы они были синхронными: вместо конечного результата асинхронного метода возвращается обещание (промис) получить результат в некоторый момент в будущем.

Promise может находиться в трёх состояниях:

  • ожидание (pending): начальное состояние, не исполнен и не отклонен.
  • исполнено (fulfilled): операция завершена успешно.
  • отклонено (rejected): операция завершена с ошибкой.

При создании промис находится в ожидании (pending), а затем может стать исполненным  (fulfilled), вернув полученный результат (значение), или отклоненным (rejected), вернув причину отказа. В любом из этих случаев вызывается обработчик, прикрепленный к промису методом then. (Если в момент назначения обработчика промис уже исполнен или отклонен, обработчик все равно будет вызван, т.е. асинхронное исполнение промиса и назначение обработчика не будет происходить в «состоянии гонки», как, например, в случае с событиями в DOM.)

Так как методы Promise.prototype.then() и Promise.prototype.catch() сами возвращают промис, их можно вызывать цепочкой, создавая соединения.

Примечание: говороят, что промис находится в состоянии завершен (settled) когда он или исполнен или отклонен, т.е. в любом состоянии, кроме ожидания (это лишь форма речи, не являющаяся настоящим состоянием промиса). Также можно встретить термин исполен (resolved) — это значит что промис завершен или "заблокирован" в ожидании завершения другого промиса. В статье состояния и fates приводится более подробное описание терминологии.

Свойства

Promise.length
Значение свойства всегда равно 1 (количество аргументов конструктора).
Promise.prototype
Представляет прототип для конструктора Promise.

Методы

Promise.all(iterable)
Ожидает исполнения всех промисов или отклонения любого из них.
Возвращает промис, который исполнится после исполнения всех промисов в iterable. В случае, если любой из промисов будет отклонен, Promise.all будет также отклонен.
Promise.allSettled(iterable)
Ожидает завершения всех полученных промисов (как исполнения так и отклонения).
Возвращает промис, который исполняется когда все полученные промисы завершены (исполнены или отклонены), содержащий массив результатов исполнения полученных промисов.
Promise.race(iterable)
Ожидает исполнения или отклонения любого из полученных промисов.
Возвращает промис, который будет исполнен или отклонен с результатом исполнения первого исполненного или отклонённого промиса из .iterable.
Promise.reject(reason)
Возвращает промис, отклонённый из-за reason.
Promise.resolve(value)
Возвращает промис, исполненный с результатом value.

Прототип Promise

Свойства

Promise.prototype.constructor
Возвращает функцию, которая создала прототип экземпляра. Это функция всех обещаний по умолчанию.

Методы

Promise.prototype.catch(onRejected)
Добавляет функцию обратного вызова для обработки отклонения обещания, которая возвращает новое обещание выполненное с переданным значением, если она вызвана, или оригинальное значение resolve, если обещание выполнено.
Promise.prototype.then(onFulfilled, onRejected)
Добавляет обработчик выполнения и отклонения обещания, и возвращает новое обещание выполненное со значением вызванного обработчика, или оригинальное значение, если обещание не было обработано (т.е. если соответствующий обработчик onFulfilled или onRejected не является функцией).

Создание промиса

Объект Promise создается при помощи ключевого слова new и своего конструктора. Конструктор Promise принимает в качестве аргумента функцию, называемую "исполнитель" (executor function). Эта функция должна принимать две функции-коллбэка в качестве параметров. Первый из них (resolve) вызывается, когда асинхронная операция завершилась успешно и вернула результат своего исполнения в виде значения. Второй коллбэк (reject) вызывается, когда операция не удалась, и возвращает значение, указывающее на причину неудачи, чаще всего объект ошибки.

const myFirstPromise = new Promise((resolve, reject) => {
  // выполняется асинхронная операция, которая в итоге вызовет:
  //
  //   resolve(someValue); // успешное завершение
  // или
  //   reject("failure reason"); // неудача
});

Чтобы снабдить функцию функционалом обещаний, нужно просто вернуть в ней объект Promise:

function myAsyncFunction(url) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open("GET", url);
    xhr.onload = () => resolve(xhr.responseText);
    xhr.onerror = () => reject(xhr.statusText);
    xhr.send();
  });
}

Примеры

Простой пример

let myFirstPromise = new Promise((resolve, reject) => {
  // Мы вызываем resolve(...), когда асинхронная операция завершилась успешно, и reject(...), когда она не удалась.
  // В этом примере мы используем setTimeout(...), чтобы симулировать асинхронный код. 
  // В реальности вы, скорее всего, будете использовать XHR, HTML5 API или что-то подобное.
  setTimeout(function(){
    resolve("Success!"); // Ура! Всё прошло хорошо!
  }, 250);
});

myFirstPromise.then((successMessage) => {
  // successMessage - это что угодно, что мы передали в функцию resolve(...) выше.
  // Это необязательно строка, но если это всего лишь сообщение об успешном завершении, это наверняка будет она.
  console.log("Ура! " + successMessage);
});

Продвинутый пример

<button id="btn">Создать Promise!</button>
<div id="log"></div>

Данный небольшой пример показывает механизм работы с Promise. Метод testPromise() вызывается при каждом нажатии на <button>. При этом создаётся промис, которое успешно выполняется при помощи window.setTimeout, со значением 'result' в случайном интервале от 1 до 3-х секунд.

исполнение промиса протоколируется при помощи продолжения p1.then. Это показывает как синхронная часть метода отвязана от асинхронного завершения промиса.

var promiseCount = 0;
function testPromise() {
  var thisPromiseCount = ++promiseCount;

  var log = document.getElementById('log');
  log.insertAdjacentHTML('beforeend', thisPromiseCount + 
      ') Запуск (запуск синхронного кода)
');

  // Создаём промис, возвращающее 'result' (по истечении 3-х секунд)
  var p1 = new Promise(
    // Функция разрешения позволяет завершить успешно или 
    // отклонить промис
    function(resolve, reject) {       
      log.insertAdjacentHTML('beforeend', thisPromiseCount + 
          ') Запуск промиса (запуск асинхронного кода)
');
      // Это всего лишь пример асинхронности
      window.setTimeout(
        function() {
          // Обещание исполнено!
          resolve(thisPromiseCount)
        }, Math.random() * 2000 + 1000);
    });

  // Указываем, что сделать с исполненным промисм
  p1.then(
    // Записываем в протокол
    function(val) {
      log.insertAdjacentHTML('beforeend', val +
          ') Обещание исполнено (асинхронный код завершён)
');
    });

  log.insertAdjacentHTML('beforeend', thisPromiseCount + 
      ') Обещание создано (синхронный код завершён)
');
}
if ("Promise" in window) {
  let btn = document.getElementById("btn");
  btn.addEventListener("click",testPromise);
} else {
  log = document.getElementById('log');
  log.innerHTML = "Демонстрация невозможна, поскольку ваш браузер не поддерживает интерфейс <code>Promise<code>.";
}

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

Загрузка изображения при помощи XHR

Другой простой пример использования Promise и XMLHttpRequest для загрузки изображения доступен в репозитории MDNpromise-test на GitHub. Вы также можете посмотреть его в действии. Каждый шаг прокомментирован и вы можете подробно исследовать Promise и XHR.

Спецификации

Спецификация Статус Комментарий
domenic/promises-unwrapping Черновик Начало работы над стандартом.
ECMAScript 2015 (6th Edition, ECMA-262)
Определение 'Promise' в этой спецификации.
Стандарт Изначальное определение в стандарте ECMA.

Совместимость с браузерами

Update compatibility data on GitHub
КомпьютерыМобильныеServer
ChromeEdgeFirefoxInternet ExplorerOperaSafariAndroid webviewChrome для AndroidFirefox для AndroidOpera для AndroidSafari on iOSSamsung InternetNode.js
PromiseChrome Полная поддержка 32Edge Полная поддержка 12Firefox Полная поддержка 29IE Нет поддержки НетOpera Полная поддержка 19Safari Полная поддержка 8WebView Android Полная поддержка 4.4.3Chrome Android Полная поддержка 32Firefox Android Полная поддержка 29Opera Android Полная поддержка ДаSafari iOS Полная поддержка 8Samsung Internet Android Полная поддержка 2.0nodejs Полная поддержка 0.12
Promise() constructorChrome Полная поддержка 32Edge Полная поддержка 12Firefox Полная поддержка 29
Замечания
Полная поддержка 29
Замечания
Замечания Constructor requires a new operator since version 37.
IE Нет поддержки НетOpera Полная поддержка 19Safari Полная поддержка 8
Замечания
Полная поддержка 8
Замечания
Замечания Constructor requires a new operator since version 10.
WebView Android Полная поддержка 4.4.3Chrome Android Полная поддержка 32Firefox Android Полная поддержка 29
Замечания
Полная поддержка 29
Замечания
Замечания Constructor requires a new operator since version 37.
Opera Android Полная поддержка ДаSafari iOS Полная поддержка 8
Замечания
Полная поддержка 8
Замечания
Замечания Constructor requires a new operator since version 10.
Samsung Internet Android Полная поддержка 2.0nodejs Полная поддержка 0.12
Замечания
Полная поддержка 0.12
Замечания
Замечания Constructor requires a new operator since version 4.
all()Chrome Полная поддержка 32Edge Полная поддержка 12Firefox Полная поддержка 29IE Нет поддержки НетOpera Полная поддержка 19Safari Полная поддержка 8WebView Android Полная поддержка 4.4.3Chrome Android Полная поддержка 32Firefox Android Полная поддержка 29Opera Android Полная поддержка ДаSafari iOS Полная поддержка 8Samsung Internet Android Полная поддержка 2.0nodejs Полная поддержка 0.12
allSettled()Chrome Полная поддержка 76Edge Нет поддержки НетFirefox Полная поддержка 71IE Нет поддержки НетOpera ? Safari ? WebView Android Полная поддержка 76Chrome Android Полная поддержка 76Firefox Android Нет поддержки НетOpera Android Полная поддержка ДаSafari iOS ? Samsung Internet Android Нет поддержки Нетnodejs Полная поддержка 12.9.0
catch()Chrome Полная поддержка 32Edge Полная поддержка 12Firefox Полная поддержка 29IE Нет поддержки НетOpera Полная поддержка 19Safari Полная поддержка 8WebView Android Полная поддержка 4.4.3Chrome Android Полная поддержка 32Firefox Android Полная поддержка 29Opera Android Полная поддержка ДаSafari iOS Полная поддержка 8Samsung Internet Android Полная поддержка 2.0nodejs Полная поддержка 0.12
finally()Chrome Полная поддержка 63Edge Полная поддержка 18Firefox Полная поддержка 58IE Нет поддержки НетOpera Полная поддержка 50Safari Полная поддержка 11.1WebView Android Полная поддержка 63Chrome Android Полная поддержка 63Firefox Android Полная поддержка 58Opera Android Полная поддержка 46Safari iOS Полная поддержка 11.3Samsung Internet Android Полная поддержка 8.0nodejs Полная поддержка 10.0.0
prototypeChrome Полная поддержка 32Edge Полная поддержка 12Firefox Полная поддержка 29IE Нет поддержки НетOpera Полная поддержка 19Safari Полная поддержка 8WebView Android Полная поддержка 4.4.3Chrome Android Полная поддержка 32Firefox Android Полная поддержка 29Opera Android Полная поддержка ДаSafari iOS Полная поддержка 8Samsung Internet Android Полная поддержка 2.0nodejs Полная поддержка 0.12
race()Chrome Полная поддержка 32Edge Полная поддержка 12Firefox Полная поддержка 29IE Нет поддержки НетOpera Полная поддержка 19Safari Полная поддержка 8WebView Android Полная поддержка 4.4.3Chrome Android Полная поддержка 32Firefox Android Полная поддержка 29Opera Android Полная поддержка ДаSafari iOS Полная поддержка 8Samsung Internet Android Полная поддержка 2.0nodejs Полная поддержка 0.12
reject()Chrome Полная поддержка 32Edge Полная поддержка 12Firefox Полная поддержка 29IE Нет поддержки НетOpera Полная поддержка 19Safari Полная поддержка 8WebView Android Полная поддержка 4.4.3Chrome Android Полная поддержка 32Firefox Android Полная поддержка 29Opera Android Полная поддержка ДаSafari iOS Полная поддержка 8Samsung Internet Android Полная поддержка 2.0nodejs Полная поддержка 0.12
resolve()Chrome Полная поддержка 32Edge Полная поддержка 12Firefox Полная поддержка 29IE Нет поддержки НетOpera Полная поддержка 19Safari Полная поддержка 8WebView Android Полная поддержка 4.4.3Chrome Android Полная поддержка 32Firefox Android Полная поддержка 29Opera Android Полная поддержка ДаSafari iOS Полная поддержка 8Samsung Internet Android Полная поддержка 2.0nodejs Полная поддержка 0.12
then()Chrome Полная поддержка 32Edge Полная поддержка 12Firefox Полная поддержка 29IE Нет поддержки НетOpera Полная поддержка 19Safari Полная поддержка 8WebView Android Полная поддержка 4.4.3Chrome Android Полная поддержка 32Firefox Android Полная поддержка 29Opera Android Полная поддержка ДаSafari iOS Полная поддержка 8Samsung Internet Android Полная поддержка 2.0nodejs Полная поддержка 0.12

Легенда

Полная поддержка  
Полная поддержка
Нет поддержки  
Нет поддержки
Совместимость неизвестна  
Совместимость неизвестна
Смотрите замечания реализации.
Смотрите замечания реализации.

Смотрите также