Skip to content

Instantly share code, notes, and snippets.

@dSalieri
Last active February 1, 2024 08:29
Show Gist options
  • Save dSalieri/baaa4efa31c821037c8fb98ee6f56985 to your computer and use it in GitHub Desktop.
Save dSalieri/baaa4efa31c821037c8fb98ee6f56985 to your computer and use it in GitHub Desktop.
Создание специальной записи возможности Promise / Promise.withResolvers момент

Изначально этот файл демонстрировал внутреннюю операцию NewPromiseCapability на языке js. Не так давно в 14 версию ECMAScript интегрировали API Promise.withResolvers. И всвязи с этим я хочу кое-что пояснить.

Для начала вспомним операцию NewPromiseCapability:

function NewPromiseCapability(C){
  if (!(C.prototype && C.prototype.constructor === C && typeof C === "function")) throw TypeError("C is not a constructor");
  const resolvingFunctions = {
    resolve: undefined,
    reject: undefined,
  };
  const closure = function(resolve, reject){
    if (resolvingFunctions.resolve !== undefined) throw TypeError("Resolve function is not undefined");
    if (resolvingFunctions.reject !== undefined) throw TypeError("Reject function is not undefined");
    resolvingFunctions.resolve = resolve;
    resolvingFunctions.reject = reject;
  };
  const promise = Reflect.construct(C, [closure]);
  if (typeof resolvingFunctions.resolve !== "function") throw TypeError("Resolve function is not callable");
  if (typeof resolvingFunctions.reject !== "function") throw TypeError("Reject function is not callable");
  return {
    "[[Promise]]": promise,
    "[[Resolve]]": resolvingFunctions.resolve,
    "[[Reject]]": resolvingFunctions.reject,
  };
}

Как вы можете заметить данная операция возвращает триплет свойств: [[Promise]], [[Resolve]] и [[Reject]] - тут все понятно.

Пример использования данной операции будет такой:

NewPromiseCapability(Promise); /// {[[Promise]]: Promise, [[Resolve]]: ƒ, [[Reject]]: ƒ}

И казалось бы при чем здесь Promise.withResolvers?! А вот при чем! Открываем спецификацию Promise.withResolvers и смотрим на шаги.

Вот демонстрация Promise.withResolvers на js:

function withResolvers() {
    const C = this;
    const promiseCapability = NewPromiseCapability(C);
    const obj = Object.create(Object.prototype);
    Object.defineProperty(obj, "promise", {
        value: promiseCapability["[[Promise]]"],
        writable: true,
        enumerable: true,
        configurable: true,
    });
    Object.defineProperty(obj, "resolve", {
        value: promiseCapability["[[Resolve]]"],
        writable: true,
        enumerable: true,
        configurable: true,
    });
    Object.defineProperty(obj, "reject", {
        value: promiseCapability["[[Reject]]"],
        writable: true,
        enumerable: true,
        configurable: true,
    });
    return obj;
}

Данную операцию можно вызвать так:

withResolvers.call(Promise); /// {promise: Promise, resolve: ƒ, reject: ƒ}

Если вы внимательно просмотрели код Promise.withResolvers на js, то вы заметили что данная функция использует операцию NewPromiseCapability, которая в своей сути и делает всю основную работу по получению необходимых значений, а Promise.withResolvers является лишь оберткой над NewPromiseCapability.

Просто посмотрите на результаты вызовов обеих операций:

NewPromiseCapability(Promise); /// {[[Promise]]: Promise, [[Resolve]]: ƒ, [[Reject]]: ƒ}
withResolvers.call(Promise); /// {promise: Promise, resolve: ƒ, reject: ƒ}

Разницы вообще по сути нет, кроме следующего:

  1. NewPromiseCapability - недоступна из коробки (и вряд ли когда будет доступна, так как это внутренняя операция), ее нужно реализовывать самостоятельно, если вам надо
  2. Promise.withResolvers - слишком свежая и без полифилов ее лучше не использовать

Вот такие дела.


Поддержка данной обертки составляет скудные ~64%, что полностью убивает ее использование без полифила. Если вам нужно закрыть дыру до 90% и больше, то предлагаю использовать вышеизложенные функции в своем коде со следующей строчкой:

Promise.withResolvers || Object.defineProperty(Promise, "withResolvers", {
  value: withResolvers,
  writable: true,
  configurable: true,
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment