Spec-Zone.ru › JavaScript

Promise

Базовый Широко доступен

Эта функция хорошо зарекомендовала себя и работает на многих устройствах и в разных версиях браузеров. Она доступна в браузерах с июля 2015 года.

Объект Promise представляет собой eventual completion (или failure) асинхронной операции и ее результат.

Чтобы узнать, как работают промисы и как их можно использовать, мы рекомендуем сначала прочитать Использование промисов.

Описание

Promise — это прокси для значения, которое не обязательно известно на момент создания промиса. Он позволяет ассоциировать обработчики с конечным успешным значением или причиной отказа асинхронного действия. Это позволяет асинхронным методам возвращать значения, как и синхронные методы: вместо немедленного возврата окончательного значения, асинхронный метод возвращает промис, который предоставит значение в будущем.

Promise находится в одном из следующих состояний:

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

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

Говорят, что промис settled (установлен), если он либо выполнен, либо отклонен, но не находится в состоянии ожидания.

Flowchart showing how the Promise state transitions between pending, fulfilled, and rejected via then/catch handlers. A pending promise can become either fulfilled or rejected. If fulfilled, the "on fulfillment" handler, or first parameter of the then() method, is executed and carries out further asynchronous actions. If rejected, the error handler, either passed as the second parameter of the then() method or as the sole parameter of the catch() method, gets executed.

Вы также услышите термин resolved (разрешенный) применительно к промисам — это означает, что промис установлен или "зафиксирован" в соответствии с конечным состоянием другого промиса, и дальнейшее его разрешение или отклонение не имеет эффекта. Документ States and fates из первоначального предложения Promise содержит более подробную информацию о терминологии промисов. В разговорной речи "разрешенные" промисы часто эквивалентны "выполненным" промисам, но, как показано в "States and fates", разрешенные промисы также могут быть в состоянии ожидания или отклонены. Например:

new Promise((resolveOuter) => {
  resolveOuter(
    new Promise((resolveInner) => {
      setTimeout(resolveInner, 1000);
    }),
  );
});

Этот промис уже разрешен на момент создания (поскольку resolveOuter вызывается синхронно), но он разрешен другим промисом и поэтому не будет выполнен до 1 секунды спустя, когда внутренний промис выполнится. На практике "разрешение" часто происходит за кулисами и не наблюдается, наблюдаются только его выполнение или отклонение.

Примечание: Несколько других языков имеют механизмы для ленивых вычислений и отложенных вычислений, которые они также называют "промисами", например, Scheme. Промисы в JavaScript представляют собой процессы, которые уже происходят и могут быть связаны с функциями обратного вызова. Если вы хотите лениво вычислить выражение, рассмотрите возможность использования функции без аргументов, например f = () => expression, для создания лениво вычисляемого выражения и f() для немедленного вычисления выражения.

Promise сам по себе не имеет первоклассного протокола для отмены, но вы можете иметь возможность напрямую отменить базовую асинхронную операцию, как правило, используя AbortController.

Цепочки промисов

Методы промисов then(), catch() и finally() используются для привязки дальнейших действий к промису, который достиг конечного состояния. Метод then() принимает до двух аргументов; первый аргумент — это функция обратного вызова для случая успешного выполнения промиса, а второй аргумент — функция обратного вызова для случая отклонения. Методы catch() и finally() внутренне вызывают then() и делают обработку ошибок менее многословной. Например, catch() — это, по сути, then() без передачи обработчика выполнения. Поскольку эти методы возвращают промисы, их можно объединять в цепочки. Например:

const myPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("foo");
  }, 300);
});

myPromise
  .then(handleFulfilledA, handleRejectedA)
  .then(handleFulfilledB, handleRejectedB)
  .then(handleFulfilledC, handleRejectedC);

Мы будем использовать следующую терминологию: исходный промис — это промис, у которого вызывается then; новый промис — это промис, возвращаемый then. Два обработчика, передаваемых в then, называются обработчик выполнения и обработчик отклонения соответственно.

Конечное состояние исходного промиса определяет, какой обработчик будет выполнен.

  • Если исходный промис выполнен, обработчик выполнения вызывается с значением выполнения.
  • Если исходный промис отклонен, обработчик отклонения вызывается с причиной отклонения.

Завершение обработчика определяет конечное состояние нового промиса.

  • Если обработчик возвращает значение thenable, новый промис устанавливается в то же состояние, что и возвращаемое значение.
  • Если обработчик возвращает не-thenable значение, новый промис выполняется с возвращаемым значением.
  • Если обработчик выбрасывает ошибку, новый промис отклоняется с выброшенной ошибкой.
  • Если для исходного промиса не прикреплен соответствующий обработчик, новый промис будет установлен в то же состояние, что и исходный промис — то есть, без обработчика отклонения, отклоненный промис остается отклоненным с той же причиной.

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

myPromise
  .then(handleFulfilledA)
  .then(handleFulfilledB)
  .then(handleFulfilledC)
  .catch(handleRejectedAny);

Используя стрелочные функции для функций обратного вызова, реализация цепочки промисов может выглядеть примерно так:

myPromise
  .then((value) => `${value} and bar`)
  .then((value) => `${value} and bar again`)
  .then((value) => `${value} and again`)
  .then((value) => `${value} and again`)
  .then((value) => {
    console.log(value);
  })
  .catch((err) => {
    console.error(err);
  });

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

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

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

const promiseA = new Promise(myExecutorFunc);
const promiseB = promiseA.then(handleFulfilled1, handleRejected1);
const promiseC = promiseA.then(handleFulfilled2, handleRejected2);

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

const promiseA = new Promise((resolve, reject) => {
  resolve(777);
});
// At this point, "promiseA" is already settled.
promiseA.then((val) => console.log("asynchronous logging has val:", val));
console.log("immediate logging");

// produces output in this order:
// immediate logging
// asynchronous logging has val: 777

Thenables

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

Для взаимодействия с существующими реализациями Promise язык позволяет использовать thenables вместо промисов. Например, Promise.resolve не только разрешает промисы, но и отслеживает thenables.

// This is not a Promises/A+ compliant thenable! It calls onFulfilled
// synchronously. For demonstration only.
const thenable = {
  then(onFulfilled, onRejected) {
    onFulfilled({
      // The thenable is fulfilled with another thenable
      then(onFulfilled, onRejected) {
        onFulfilled(42);
      },
    });
  },
};

Promise.resolve(thenable); // A promise fulfilled with 42

Метод then() отвечает за планирование выполнения предоставленных колбэков onFulfilled и onRejected. Его семантика, включая обработку ошибок и асинхронность, точно определена в спецификации Promises/A+, и мы не будем повторять их здесь. Очень редко возникает необходимость реализовать thenable самостоятельно; даже если вы не используете нативные промисы, вы, вероятно, будете использовать библиотеку Promise, такую как Bluebird.

Параллелизм промисов

Класс Promise предлагает четыре основных статических метода для облегчения параллелизма асинхронных задач:

Promise.all()
Выполняется, когда все промисы выполнены; отклоняется, когда любой из промисов отклонен.
Promise.allSettled()
Выполняется, когда все промисы установлены.
Promise.any()
Выполняется, когда любой из промисов выполнен; отклоняется, когда все промисы отклонены.
Promise.race()
Устанавливается, когда любой из промисов устанавливается. Другими словами, выполняется, когда любой из промисов выполняется; отклоняется, когда любой из промисов отклоняется.

Все эти методы принимают итерируемый набор промисов (точнее, thenables) и возвращают новый промис. Все они поддерживают подклассинг, что означает, что их можно вызывать для подклассов Promise, и результатом будет промис подкласса. Для этого конструктор подкласса должен реализовывать ту же сигнатуру, что и конструктор Promise() — принимая единственную функцию executor, которую можно вызвать с параметрами resolve и reject. Подкласс также должен иметь статический метод resolve, который можно вызывать как Promise.resolve() для разрешения значений в промисы.

Существуют два других удобных статических метода: Promise.allKeyed() и Promise.allSettledKeyed(), которые ведут себя как Promise.all() и Promise.allSettled(), но принимают объекты промисов и возвращают промисы, которые выполняются с объектами той же формы. Работая с объектами вместо массивов, вы можете связывать результаты с семантически значимыми ключами, а не с произвольным порядком массива, который может быть сложно поддерживать.

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

const pendingPromise = new Promise(() => {});

for (let i = 0; i < 1000; i++) {
  await Promise.race([Promise.resolve(0), pendingPromise]);
}
// All tasks have completed, but pendingPromise retains the
// handlers attached by all 1000 races.

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

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

Конструктор

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

Статические свойства

Promise[Symbol.species]
Возвращает конструктор, используемый для создания возвращаемых значений из методов промисов.

Статические методы

Promise.all()
Принимает итерируемый набор промисов в качестве входных данных и возвращает один Promise. Этот возвращаемый промис выполняется, когда все входные промисы выполняются (включая случай, когда передается пустой итерируемый набор), с массивом значений выполнения. Он отклоняется, когда любой из входных промисов отклоняется, с первой причиной отклонения.
Promise.allKeyed() Экспериментальный
Подобно Promise.all(), за исключением того, что он принимает объект промисов и возвращает промис, который выполняется с объектом той же формы, позволяя вам связывать результаты с семантически значимыми ключами.
Promise.allSettled()
Принимает итерируемый набор промисов в качестве входных данных и возвращает один Promise. Этот возвращаемый промис выполняется, когда все входные промисы устанавливаются (включая случай, когда передается пустой итерируемый набор), с массивом объектов, описывающих результат каждого промиса.
Promise.allSettledKeyed() Экспериментальный
Подобно Promise.allSettled(), за исключением того, что он принимает объект промисов и возвращает промис, который выполняется с объектом той же формы, позволяя вам связывать результаты с семантически значимыми ключами.
Promise.any()
Принимает итерируемый набор промисов в качестве входных данных и возвращает один Promise. Этот возвращаемый промис выполняется, когда любой из входных промисов выполняется, с этим первым значением выполнения. Он отклоняется, когда все входные промисы отклоняются (включая случай, когда передается пустой итерируемый набор), с AggregateError, содержащим массив причин отклонения.
Promise.race()
Принимает итерируемый набор промисов в качестве входных данных и возвращает один Promise. Этот возвращаемый промис устанавливается в конечном состоянии первого установившегося промиса.
Promise.reject()
Возвращает новый объект Promise, который отклонен с указанной причиной.
Promise.resolve()
Возвращает объект Promise, который разрешен с указанным значением. Если значение является thenable (то есть имеет метод then), возвращаемый промис будет "следовать" за этим thenable, принимая его конечное состояние; в противном случае возвращаемый промис будет выполнен с указанным значением.
Promise.try()
Принимает колбэк любого типа (возвращает или выбрасывает, синхронно или асинхронно) и разрешает его результат в Promise.
Promise.withResolvers()
Возвращает объект, содержащий новый объект Promise и две функции для его разрешения или отклонения, соответствующие двум параметрам, передаваемым исполнителю конструктора Promise().

Свойства экземпляра

Эти свойства определены на Promise.prototype и разделяются всеми экземплярами Promise.

Promise.prototype.constructor
Конструктор, который создал объект экземпляра. Для экземпляров Promise начальным значением является конструктор Promise.
Promise.prototype[Symbol.toStringTag]
Начальным значением свойства [Symbol.toStringTag] является строка "Promise". Это свойство используется в Object.prototype.toString().

Методы экземпляра

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

Примеры

Базовый пример

В этом примере мы используем setTimeout(...) для имитации асинхронного кода. В действительности вы, вероятно, будете использовать что-то вроде XHR или HTML API.

const myFirstPromise = new Promise((resolve, reject) => {
  // We call resolve(...) when what we were doing asynchronously
  // was successful, and reject(...) when it failed.
  setTimeout(() => {
    resolve("Success!"); // Yay! Everything went well!
  }, 250);
});

myFirstPromise.then((successMessage) => {
  // successMessage is whatever we passed in the resolve(...) function above.
  // It doesn't have to be a string, but if it is only a succeed message, it probably will be.
  console.log(`Yay! ${successMessage}`);
});

Пример с различными ситуациями

Этот пример демонстрирует различные методы использования возможностей Promise и различные ситуации, которые могут возникнуть. Чтобы понять это, начните с прокрутки вниз блока кода и изучите цепочку промисов. После предоставления исходного промиса может следовать цепочка промисов. Цепочка состоит из вызовов .then() и обычно (но не обязательно) имеет единственный .catch() в конце, за которым опционально следует .finally(). В этом примере цепочка промисов инициируется пользовательским конструктом new Promise(); но на практике цепочки промисов чаще начинаются с API-функции (написанной кем-то другим), которая возвращает промис.

Пример функции tetheredGetNumber() показывает, что генератор промисов будет использовать reject() при настройке асинхронного вызова, или внутри колбэка, или обоих. Функция promiseGetWord() иллюстрирует, как API-функция может генерировать и возвращать промис в самодостаточном виде.

Обратите внимание, что функция troubleWithGetNumber() заканчивается throw. Это принудительно, потому что цепочка промисов проходит через все промисы .then(), даже после ошибки, и без throw ошибка казалась бы "исправленной". Это неудобно, и по этой причине принято опускать onRejected по всей цепочке промисов .then() и иметь только один onRejected в конечном catch().

Этот код можно запустить в Node.js. Понимание улучшается при фактическом наблюдении ошибок. Чтобы вызвать больше ошибок, измените значения threshold.

// To experiment with error handling, "threshold" values cause errors randomly
const THRESHOLD_A = 8; // can use zero 0 to guarantee error

function tetheredGetNumber(resolve, reject) {
  setTimeout(() => {
    const randomInt = Date.now();
    const value = randomInt % 10;
    if (value < THRESHOLD_A) {
      resolve(value);
    } else {
      reject(new RangeError(`Too large: ${value}`));
    }
  }, 500);
}

function determineParity(value) {
  const isOdd = value % 2 === 1;
  return { value, isOdd };
}

function troubleWithGetNumber(reason) {
  const err = new Error("Trouble getting number", { cause: reason });
  console.error(err);
  throw err;
}

function promiseGetWord(parityInfo) {
  return new Promise((resolve, reject) => {
    const { value, isOdd } = parityInfo;
    if (value >= THRESHOLD_A - 1) {
      reject(new RangeError(`Still too large: ${value}`));
    } else {
      parityInfo.wordEvenOdd = isOdd ? "odd" : "even";
      resolve(parityInfo);
    }
  });
}

new Promise(tetheredGetNumber)
  .then(determineParity, troubleWithGetNumber)
  .then(promiseGetWord)
  .then((info) => {
    console.log(`Got: ${info.value}, ${info.wordEvenOdd}`);
    return info;
  })
  .catch((reason) => {
    if (reason.cause) {
      console.error("Had previously handled error");
    } else {
      console.error(`Trouble with promiseGetWord(): ${reason}`);
    }
  })
  .finally((info) => console.log("All done"));

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

Этот небольшой пример показывает механизм Promise. Метод testPromise() вызывается каждый раз при нажатии на <button>. Он создает промис, который будет выполнен, используя setTimeout(), на счетчик промисов (число, начиная с 1) каждые 1-3 секунды, случайным образом. Конструктор Promise() используется для создания промиса.

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

Нажав кнопку несколько раз за короткое время, вы даже увидите, как разные промисы выполняются один за другим.

HTML

<button id="make-promise">Make a promise!</button>
<div id="log"></div>

JavaScript

"use strict";

let promiseCount = 0;

function testPromise() {
  const thisPromiseCount = ++promiseCount;
  const log = document.getElementById("log");
  // begin
  log.insertAdjacentHTML("beforeend", `${thisPromiseCount}) Started<br>`);
  // We make a new promise: we promise a numeric count of this promise,
  // starting from 1 (after waiting 3s)
  const p1 = new Promise((resolve, reject) => {
    // The executor function is called with the ability
    // to resolve or reject the promise
    log.insertAdjacentHTML(
      "beforeend",
      `${thisPromiseCount}) Promise constructor<br>`,
    );
    // This is only an example to create asynchronism
    setTimeout(
      () => {
        // We fulfill the promise
        resolve(thisPromiseCount);
      },
      Math.random() * 2000 + 1000,
    );
  });

  // We define what to do when the promise is resolved with the then() call,
  // and what to do when the promise is rejected with the catch() call
  p1.then((val) => {
    // Log the fulfillment value
    log.insertAdjacentHTML("beforeend", `${val}) Promise fulfilled<br>`);
  }).catch((reason) => {
    // Log the rejection reason
    console.log(`Handle rejected promise (${reason}) here.`);
  });
  // end
  log.insertAdjacentHTML("beforeend", `${thisPromiseCount}) Promise made<br>`);
}

const btn = document.getElementById("make-promise");
btn.addEventListener("click", testPromise);

Результат

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

Ниже приведен другой пример использования Promise и XMLHttpRequest для загрузки изображения. Каждый шаг комментируется и позволяет внимательно следить за архитектурой Promise и XHR.

<h1>Promise example</h1>
function imgLoad(url) {
  // Create new promise with the Promise() constructor;
  // This has as its argument a function with two parameters, resolve and reject
  return new Promise((resolve, reject) => {
    // XHR to load an image
    const request = new XMLHttpRequest();
    request.open("GET", url);
    request.responseType = "blob";
    // When the request loads, check whether it was successful
    request.onload = () => {
      if (request.status === 200) {
        // If successful, resolve the promise by passing back the request response
        resolve(request.response);
      } else {
        // If it fails, reject the promise with an error message
        reject(
          Error(
            `Image didn't load successfully; error code: + ${request.statusText}`,
          ),
        );
      }
    };
    // Handle network errors
    request.onerror = () => reject(new Error("There was a network error."));
    // Send the request
    request.send();
  });
}

// Get a reference to the body element, and create a new image object
const body = document.querySelector("body");
const myImage = new Image();
const imgUrl =
  "https://mdn.github.io/shared-assets/images/examples/round-balloon.png";

// Call the function with the URL we want to load, then chain the
// promise then() method with two callbacks
imgLoad(imgUrl).then(
  (response) => {
    // The first runs when the promise resolves, with the request.response
    // specified within the resolve() method.
    const imageURL = URL.createObjectURL(response);
    myImage.src = imageURL;
    body.appendChild(myImage);
  },
  (error) => {
    // The second runs when the promise
    // is rejected, and logs the Error specified with the reject() method.
    console.log(error);
  },
);

Отслеживание текущего объекта настроек

Объект настроек — это окружение, которое предоставляет дополнительную информацию при выполнении кода JavaScript. Это включает в себя realm и карту модулей, а также специфичную для HTML информацию, такую как origin. Текущий объект настроек отслеживается, чтобы гарантировать, что браузер знает, какой из них использовать для данного фрагмента пользовательского кода.

Чтобы лучше понять это, мы можем более подробно рассмотреть, как realm может быть проблемой. Realm можно примерно представить как глобальный объект. Уникальность realms заключается в том, что они содержат всю необходимую информацию для выполнения кода JavaScript. Это включает такие объекты, как Array и Error. Каждый объект настроек имеет свою "копию" этих объектов, и они не разделяются. Это может привести к неожиданному поведению в отношении промисов. Чтобы обойти это, мы отслеживаем так называемый текущий объект настроек. Он представляет информацию, специфичную для контекста пользовательского кода, отвечающего за определенный вызов функции.

Чтобы проиллюстрировать это дальше, мы можем взглянуть на то, как <iframe>, встраиваемый в документ, взаимодействует со своим хостом. Поскольку все веб-API осведомлены о текущем объекте настроек, следующее будет работать во всех браузерах:

<!doctype html>
<iframe></iframe>
<!-- we have a realm here -->
<script>
  // we have a realm here as well
  const bound = frames[0].postMessage.bind(frames[0], "some data", "*");
  // bound is a built-in function — there is no user
  // code on the stack, so which realm do we use?
  setTimeout(bound);
  // this still works, because we use the youngest
  // realm (the incumbent) on the stack
</script>

Та же концепция применима и к промисам. Если мы немного изменим предыдущий пример, мы получим:

<!doctype html>
<iframe></iframe>
<!-- we have a realm here -->
<script>
  // we have a realm here as well
  const bound = frames[0].postMessage.bind(frames[0], "some data", "*");
  // bound is a built in function — there is no user
  // code on the stack — which realm do we use?
  Promise.resolve(undefined).then(bound);
  // this still works, because we use the youngest
  // realm (the incumbent) on the stack
</script>

Если мы изменим это так, чтобы <iframe> в документе прослушивал post messages, мы сможем наблюдать эффект текущего объекта настроек:

<!-- y.html -->
<!doctype html>
<iframe src="x.html"></iframe>
<script>
  const bound = frames[0].postMessage.bind(frames[0], "some data", "*");
  Promise.resolve(undefined).then(bound);
</script>
<!-- x.html -->
<!doctype html>
<script>
  window.addEventListener("message", (event) => {
    document.querySelector("#text").textContent = "hello";
    // this code will only run in browsers that track the incumbent settings object
    console.log(event);
  });
</script>

В приведенном выше примере внутренний текст <iframe> будет обновлен только в том случае, если отслеживается текущий объект настроек. Это потому, что без отслеживания текущего объекта мы можем в конечном итоге использовать неправильное окружение для отправки сообщения.

Примечание: В настоящее время отслеживание текущего realm полностью реализовано в Firefox и имеет частичные реализации в Chrome и Safari.

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

Спецификация
ECMAScript® 2027 Language Specification
# sec-promise-objects

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

Desktop Мобильный Сервер
Chrome Edge Firefox Opera Safari Chrome Android Firefox for Android Opera Android Safari on iOS Samsung Internet WebView Android WebView on iOS Bun Deno Node.js
Promise
32
12
29
19
19
32
29
19
8
2.0
4.4.3
8
1.0.0
1.0
0.12.0
@@species
51
79
48
38
10
51
48
41
10
5.0
51
10
1.0.0
1.0
6.5.0
Promise
32
12
29Конструктор требует оператор new начиная с версии 37.
19
8Конструктор требует оператор new начиная с версии 10.
32
29Конструктор требует оператор new начиная с версии 37.
19
8Конструктор требует оператор new начиная с версии 10.
2.0
4.4.3
8Конструктор требует оператор new начиная с версии 10.
1.0.0
1.0
0.12.0Конструктор требует оператор new начиная с версии 4.
all
32
12
29
19
8
32
29
19
8
2.0
4.4.3
8
1.0.0
1.0
0.12.0
allKeyed
Нет
Нет
155
Нет
Нет
Нет
155
Нет
Нет
Нет
Нет
Нет
?
?
?
allSettled
76
79
71
63
13
76
79
54
13
12.0
76
13
1.0.0
1.0
12.9.0
allSettledKeyed
Нет
Нет
155
Нет
Нет
Нет
155
Нет
Нет
Нет
Нет
Нет
?
?
?
any
85
85
79
71
14
85
79
60
14
14.0
85
14
1.0.0
1.2
15.0.0
catch
32
12
29
19
8
32
29
19
8
2.0
4.4.3
8
1.0.0
1.0
0.12.0
finally
63
18
58
50
11.1
63
58
46
11.3
8.0
63
11.3
1.0.0
1.0
10.0.0
incumbent_settings_object_tracking
Нет
Нет
50
Нет
Нет
Нет
50
Нет
Нет
Нет
Нет
Нет
?
Нет
Нет
race
32
12
29
19
8
32
29
19
8
2.0
4.4.3
8
1.0.0
1.0
0.12.0
reject
32
12
29
19
8
32
29
19
8
2.0
4.4.3
8
1.0.0
1.0
0.12.0
resolve
32
12
29
19
8
32
29
19
8
2.0
4.4.3
8
1.0.0
1.0
0.12.0
then
32
12
29
19
8
32
29
19
8
2.0
4.4.3
8
1.0.0
1.0
0.12.0
try
128
128
134
114
18.2
128
134
85
18.2
28.0
128
18.2
1.1.22
1.46
23.0.0
withResolvers
119
119
121
105
17.4
119
121
79
17.4
25.0
119
17.4
1.0.0
1.38
22.0.0

См. также

  • Polyfill Promise в core-js
  • Руководство по использованию промисов
  • Спецификация Promises/A+
  • JavaScript Promises: an introduction на web.dev (2013)
  • Callbacks, Promises, and Coroutines: Asynchronous Programming Patterns in JavaScript презентация Доменика ДеНиколы (2011)

© 2005–2025 MDN contributors.
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API