Разрешить Javascript Promise вне области действия функции

Я использовал ES6 Promise.

Обычно Promise создается и используется следующим образом

new Promise(function(resolve, reject){
    if (someCondition){
        resolve();
    } else {
        reject();
    } 
});

Но я делал что-то вроде ниже, чтобы принять решение за пределами ради гибкости.

var outsideResolve;
var outsideReject;
new Promise(function(resolve, reject) { 
    outsideResolve = resolve; 
    outsideReject = reject; 
});

И позже

onClick = function(){
    outsideResolve();
}

Это нормально работает, но есть ли более простой способ сделать это? Если нет, то это хорошая практика?


person Morio    schedule 01.10.2014    source источник
comment
Я не думаю, что есть другой выход. Я считаю, что указано, что обратный вызов, переданный в Promise, должен выполняться синхронно, чтобы можно было экспортировать две функции.   -  person Felix Kling    schedule 02.10.2014
comment
Для меня это работает точно так же, как вы это написали. Насколько я понимаю, это канонический путь.   -  person Gilad Barner    schedule 31.07.2016
comment
Я думаю, что в будущем должен быть формальный способ добиться этого. На мой взгляд, эта функция очень мощная, поскольку вы можете ждать значений из других контекстов.   -  person Jose    schedule 03.04.2018
comment
Всякий раз, когда они придумывают правильное решение этой проблемы, я надеюсь, они также заставят его работать для вложенных обещаний, некоторые из которых могут повторяться.   -  person Arthur Tarasov    schedule 05.07.2018
comment
Я думаю, что Promise API предлагает всегда использовать их как возвращаемые значения, а не как объекты, к которым вы можете получить доступ или вызвать. Другими словами, заставляют нас рассматривать их как возвращаемые значения вместо объектов, к которым мы можем получить доступ, или функций, которые мы можем вызывать, или чего-то, что мы можем ссылаться с помощью переменной или передавать в качестве параметра и т. Д. Если вы начнете использовать обещания как любой другой объект, вероятно, вы будете в конечном итоге необходимо решить это извне, как в вашем вопросе ... При этом я также думаю, что должен быть формальный способ сделать это ... и Deferred кажется мне просто обходным путем.   -  person cancerbero    schedule 14.05.2019
comment
@Jose Для меня это было полезно пару раз, когда мне нужно, чтобы Promise разрешалось сразу после другого обещания, в случаях, когда Promise.all или Promise.race не работают.   -  person JulianSoto    schedule 17.03.2021


Ответы (22)


просто:

var promiseResolve, promiseReject;

var promise = new Promise(function(resolve, reject){
  promiseResolve = resolve;
  promiseReject = reject;
});

promiseResolve();
person carter    schedule 17.03.2016
comment
@ruX, как упоминается в принятом ответе - это было сделано специально. Дело в том, что если генерируется исключение, оно будет перехвачено конструктором обещания. Этот ответ (как и мой) имеет ловушку, заключающуюся в том, что он может генерировать исключение для любого кода, вызывающего promiseResolve(). Семантика обещания такова, что оно всегда возвращает значение. Кроме того, функционально это то же самое, что и сообщение OP, я не понимаю, какую проблему это решает повторно. - person Jon Jaques; 27.06.2016
comment
@JonJaques Я не уверен, правда ли то, что вы говорите. Код, вызывающий promiseResolve(), не вызовет исключения. Вы можете определить .catch в конструкторе, и независимо от того, какой код вызывает его, будет вызываться .catch конструктора. Вот jsbin, демонстрирующий, как это работает: jsbin.com/yicerewivo/edit?js,console - person carter; 06.07.2016
comment
Теперь я понимаю, что вы имеете в виду, но не понимаю, как это связано с созданием гибких преобразователей обещаний. Если вы выдаете ошибку за пределами того, что может ее отловить, то она не будет обнаружена. Это не ловушка обещания. Это просто пример плохого паттерна. - person carter; 12.07.2016
comment
Я даже не уверен, что это плохой дизайн. Ошибка, возникшая за пределами обещания, не должна быть обнаружена внутри обещания. Возможно, это пример заблуждения или плохого понимания, если дизайнер на самом деле ожидает, что ошибка будет обнаружена внутри. - person KalEl; 12.05.2017
comment
Эта точная конструкция уже упоминается в вопросе. Вы вообще это читали? - person Cedric Reichenbach; 30.09.2017
comment
@carter, а что если обещание было создано (async()=>{})()? - person Pacerier; 16.10.2017

Немного поздно для вечеринки, но другой способ сделать это - использовать объект Deferred. . По сути, у вас такое же количество шаблонов, но это удобно, если вы хотите передать их и, возможно, разрешить за пределами их определения.

Простая реализация:

class Deferred {
  constructor() {
    this.promise = new Promise((resolve, reject)=> {
      this.reject = reject
      this.resolve = resolve
    })
  }
}

function asyncAction() {
  var dfd = new Deferred()

  setTimeout(()=> {
    dfd.resolve(42)
  }, 500)

  return dfd.promise
}

asyncAction().then(result => {
  console.log(result) // 42
})

Версия ES5:

function Deferred() {
  var self = this;
  this.promise = new Promise(function(resolve, reject) {
    self.reject = reject
    self.resolve = resolve
  })
}

function asyncAction() {
  var dfd = new Deferred()

  setTimeout(function() {
    dfd.resolve(42)
  }, 500)

  return dfd.promise
}

asyncAction().then(function(result) {
  console.log(result) // 42
})
person Jon Jaques    schedule 06.01.2016
comment
Обратите внимание на лексическую область видимости здесь. - person Florrie; 02.02.2016
comment
Нет никакой практической разницы в том, назначается resolve|reject лексически или через bind. Это простая реализация объекта jQuery Deferred, который существует с 1.0 (ish ). Это работает точно так же, как и обещание, за исключением того, что здесь нет защиты от броска. Весь смысл этого вопроса состоял в том, как сэкономить несколько строк кода при создании обещаний. - person Jon Jaques; 13.07.2016
comment
Использование deferred - это обычный способ сделать это, я понятия не имею, почему это не выше - person BlueRaja - Danny Pflughoeft; 01.03.2017
comment
Отличный ответ! Искал отложенную функциональность, которую предлагает jQuery. - person Anshul Koka; 29.03.2017
comment
Deferred устарело? - person Pacerier; 16.10.2017
comment
@Pacerier в jQuery? наверное. Однако это чистая реализация поверх современных API. Как видите, в этом нет ничего особенного. - person Jon Jaques; 19.01.2018
comment
Я не знаю, принято ли это сейчас или нет, но это работает очень хорошо, так что меня это устраивает. Пока проблем нет. - person beatcoder; 25.12.2019
comment
Принято? Мех. Просто имейте в виду, что если функция asyncAction (вещь, которая создает отложенный объект) выдает исключение, кроме как во время создания, вы не получите обещание. Просто будь осторожен с тем, что делаешь;) - person Jon Jaques; 17.01.2020

Нет, другого способа сделать это нет - единственное, что я могу сказать, это то, что этот вариант использования не очень распространен. Как сказал Феликс в комментарии - то, что вы делаете, будет работать постоянно.

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

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

var p = new Promise(function(resolve, reject){
    this.onclick = resolve;
}.bind(this));

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

Обратите внимание, что вы никогда не должны использовать конструктор обещаний для таких вещей, как if(condition), первый пример можно записать как:

var p = Promise[(someCondition)?"resolve":"reject"]();
person Benjamin Gruenbaum    schedule 01.10.2014
comment
Привет, Бенджамин! Есть ли в настоящее время лучший способ получить вкусный сахар для обещаний, если мы еще не знаем, когда обещание будет выполнено? Как какой-то асинхронный шаблон ожидания / уведомления? Например, сохранить, а затем вызвать цепочку Promise? Например. в моем конкретном случае я нахожусь на сервере, ожидая определенного ответа клиента (типа SYN-ACK-типа рукопожатия, чтобы убедиться, что клиент успешно обновил состояние). - person Domi; 03.05.2015
comment
@Domi проверил q-соединение и RxJS. - person Benjamin Gruenbaum; 03.05.2015
comment
Ха, как обычно, мне не терпелось ждать! В любом случае кажется, что то, что делают эти библиотеки, является излишним для моего конкретного случая использования, если только они не исправляют проблемы, которые я упустил. Не могли бы вы взглянуть на мое минимальное решение? - person Domi; 03.05.2015
comment
Как я мог сделать то же самое, используя API выборки? - person Vinod Sobale; 20.04.2017
comment
Необычно? Мне он нужен почти для каждого проекта. - person Tomáš Zato - Reinstate Monica; 23.06.2017
comment
@ TomášZato интересно, я бы хотел узнать о некоторых случаях использования этого - если бы вы могли создать несколько примеров на сущности Я обязательно посмотрю и передам ваши вопросы в технический комитет. - person Benjamin Gruenbaum; 23.06.2017
comment
Я не понимаю, как эту последнюю строку кода можно считать хорошим способом написания кода. Это очень трудно читать. Я бы пошел на подробный читаемый способ, как это сделал OP. - person Gherman; 24.10.2017
comment
Что касается варианта использования, подумайте, что вам нужно что-то сделать после того, как событие сработало и произошло что-то еще. Вы хотите превратить событие в обещание и объединить его с другим обещанием. Для меня это проблема общего характера. - person Gherman; 24.10.2017
comment
Похоже, что Promise API был разработан для предотвращения вызова метода resolve () извне конструктора. Но нередко бывает, что для определения асинхронного API совместно работают более одного актора: один знает, когда устанавливать обещание, а другой знает, как его решить. И я не хочу добавлять эмиттер посередине только для этого, и дизайн не запрещает мне его взламывать, поэтому мне интересно, какой шаблон здесь будет правильным. Будет ли это класс Deferred, как предлагается ниже? Должен ли я использовать эмиттер событий, как в старые времена, и потерять синтаксис async / await? Любые идеи? - person cancerbero; 14.05.2019
comment
@cancerbero API обещаний был разработан для использования таким образом, потому что если функция, переданная в new Promise, выдает, вы получите отклоненное обещание. Он также является явно синхронным, и вы можете положиться на отложенного партнера. - person Benjamin Gruenbaum; 14.05.2019
comment
@BenjaminGruenbaum - Моим первым вариантом использования было ожидание загрузки шрифтов в сочетании с другими асинхронными процессами. Это позволяет мне создать один обратный вызов для завершения определенных комбинаций шрифтов и другого кода. Я делал это с двумя глобальными int: totalCount и loadedCount. Я бы ++loadedCount в document.fonts.ready.then() и в обработчиках событий для асинхронных процессов. Теперь мне не нужно отслеживать, сколько процессов находится в каждой группе. Помимо загрузки шрифтов, теперь я использую его для создания одного обратного вызова для нескольких запросов AJAX. Я добавил ответ с примером шрифтов. - person jamess; 11.06.2019
comment
p-defer работал у меня: npmjs.com/package/p-defer после этого решение сработало, и я подумал, что, может быть, там что-то уже построено. - person Chris; 14.08.2019
comment
this.onclick = resolve; вызовет resolve функцию в обещании при каждом щелчке, что может быть накладным расходом, поскольку обещание может быть выполнено только один раз. Не повредит, но и не самое эффективное решение. Особенно, если вы делаете это таким образом, чтобы отловить возможные ошибки, которые в этой строке не возникнут в любом случае. - person Hrvoje Golcic; 21.06.2020
comment
Возможно, написание var p = Promise[(someCondition)?"resolve":"reject"](); может привести к менее читаемому и удобному в сопровождении коду. Конечно, это всегда зависит от конкретного варианта использования, но на самом деле здесь нет нужды фантазировать. Такой код был написан здесь, например, конечно, нет смысла давать обещание, которое работает таким образом. - person Hrvoje Golcic; 21.06.2020
comment
@BenjaminGruenbaum - Другой вариант использования - если вы общаетесь с веб-воркером. Если вы ожидаете, что через сообщения веб-воркера будет поступать несколько частей информации (в неизвестном порядке), неплохо дать обещание p [i] для каждой части информации, чтобы потребители этой информации могли дождаться этого обещания или зарегистрировать обратный вызов через p [i] .then (callme). Это обещание необходимо разрешить с помощью worker.onmessage, а не с помощью кода, предоставленного при создании обещания. (Или отклоняется кодом в worker.onerror.) Обычно каждый раз, когда асинхронный процесс запускает несколько неупорядоченных обратных вызовов, вам нужно то, о чем говорит OP. - person Matt; 18.03.2021
comment
Было бы так удобно, если бы вы могли просто var p = new Promise(); p.resolve() - person br4nnigan; 14.04.2021

Решение, которое я придумал для своего фреймворка в 2015 году. Я назвал этот тип обещаний Задачей.

function createPromise(handler){
  var resolve, reject;

  var promise = new Promise(function(_resolve, _reject){
    resolve = _resolve; 
    reject = _reject;
    if(handler) handler(resolve, reject);
  })
  
  promise.resolve = resolve;
  promise.reject = reject;
  return promise;
}


// create
var promise = createPromise()
promise.then(function(data){ alert(data) })

// resolve from outside
promise.resolve(200)
person Maxmaxmaximus    schedule 28.07.2017
comment
Спасибо, это сработало. Но что такое хендлер? Мне пришлось удалить его, чтобы он заработал. - person Sahid; 01.02.2019
comment
@Sahid, когда вы запускаете createPromise (), вам нужно передать ему функцию в качестве аргумента. иначе код не работает. У вас может быть оператор if и проверять правильность аргумента обработчика перед его вызовом. - person Michael Mammoliti; 15.09.2020
comment
Спасибо за код! Но разве какой-то другой код не может вызвать ваш .resolve до того, как его установит обратный вызов? Я привык к обычным потокам, а не к асинхронным событиям, поэтому могу немного запутаться. - person AnthonyD973; 29.10.2020

Мне понравился ответ @JonJaques, но я хотел сделать еще один шаг.

Если вы связываете then и catch, тогда объект Deferred, тогда он полностью реализует Promise API, и вы можете рассматривать его как обещание, await и тому подобное.

class DeferredPromise {
  constructor() {
    this._promise = new Promise((resolve, reject) => {
      // assign the resolve and reject functions to `this`
      // making them usable on the class instance
      this.resolve = resolve;
      this.reject = reject;
    });
    // bind `then` and `catch` to implement the same interface as Promise
    this.then = this._promise.then.bind(this._promise);
    this.catch = this._promise.catch.bind(this._promise);
    this[Symbol.toStringTag] = 'Promise';
  }
}

const deferred = new DeferredPromise();
console.log('waiting 2 seconds...');
setTimeout(() => {
  deferred.resolve('whoa!');
}, 2000);

async function someAsyncFunction() {
  const value = await deferred;
  console.log(value);
}

someAsyncFunction();

person Rico Kahler    schedule 04.11.2017
comment
Мне это очень нравится. Спасибо. Я использую его как настраиваемый компонент в своем приложении Express, но он был бы прекрасен в качестве модуля NPM, если бы вы захотели его создать, или я мог бы, если бы это было необходимо. Этот подход представляет собой отличное сочетание нового async / await и того, как старая платформа Parse Platform использовалась для подхода к обещаниям en.wikipedia.org/wiki/Parse_ (платформа) - person Michael Kubler; 02.08.2020

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

const createPromise = () => {
    let resolver;
    return [
        new Promise((resolve, reject) => {
            resolver = resolve;
        }),
        resolver,
    ];
};

const [ promise, resolver ] = createPromise();
promise.then(value => console.log(value));
setTimeout(() => resolver('foo'), 1000);

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

Через одну секунду консоль выдаст:

> foo
person Ali    schedule 07.08.2019
comment
Думаю, это лучший подход. Единственное, что код может быть немного менее подробным. - person Adam Pietrasiak; 12.09.2019
comment
Отлично! Умная идея. +50, если бы мог. - person Mitya; 12.03.2020
comment
Это именно то, что сделал OP. На самом деле вы заново изобретаете отложенный шаблон над обещаниями, конечно, это возможно, и ваш подход работает (как исходный код OP), но это не лучшая практика из-за причины безопасности, описанной в принятом ответе. - person dhilt; 21.05.2020

Вспомогательный метод уменьшит эти дополнительные накладные расходы и даст вам то же ощущение jQuery.

function Deferred() {
    let resolve;
    let reject;
    const promise = new Promise((res, rej) => {
        resolve = res;
        reject = rej;
    });
    return { promise, resolve, reject };
}

Использование будет

const { promise, resolve, reject } = Deferred();
displayConfirmationDialog({
    confirm: resolve,
    cancel: reject
});
return promise;

Что похоже на jQuery

const dfd = $.Deferred();
displayConfirmationDialog({
    confirm: dfd.resolve,
    cancel: dfd.reject
});
return dfd.promise();

Хотя в случае использования этот простой нативный синтаксис вполне подойдет

return new Promise((resolve, reject) => {
    displayConfirmationDialog({
        confirm: resolve,
        cancel: reject
    });
});
person Cory Danielson    schedule 03.02.2018

Я использую вспомогательную функцию для создания того, что я называю «плоским обещанием» -

function flatPromise() {

    let resolve, reject;

    const promise = new Promise((res, rej) => {
      resolve = res;
      reject = rej;
    });

    return { promise, resolve, reject };
}

И я использую это так -

function doSomethingAsync() {

    // Get your promise and callbacks
    const { resolve, reject, promise } = flatPromise();

    // Do something amazing...
    setTimeout(() => {
        resolve('done!');
    }, 500);

    // Pass your promise to the world
    return promise;

}

См. Полный рабочий пример -

function flatPromise() {

    let resolve, reject;

    const promise = new Promise((res, rej) => {
        resolve = res;
        reject = rej;
    });

    return { promise, resolve, reject };
}

function doSomethingAsync() {
    
    // Get your promise and callbacks
    const { resolve, reject, promise } = flatPromise();

    // Do something amazing...
    setTimeout(() => {
        resolve('done!');
    }, 500);

    // Pass your promise to the world
    return promise;
}

(async function run() {

    const result = await doSomethingAsync()
        .catch(err => console.error('rejected with', err));
    console.log(result);

})();

Изменить: я создал пакет NPM под названием плоское обещание, и код также доступен на GitHub.

person Arik    schedule 19.11.2018

Вы можете обернуть Promise в класс.

class Deferred {
    constructor(handler) {
        this.promise = new Promise((resolve, reject) => {
            this.reject = reject;
            this.resolve = resolve;
            handler(resolve, reject);
        });

        this.promise.resolve = this.resolve;
        this.promise.reject = this.reject;

        return this.promise;
    }
    promise;
    resolve;
    reject;
}

// How to use.
const promise = new Deferred((resolve, reject) => {
  // Use like normal Promise.
});

promise.resolve(); // Resolve from any context.
person Hinrich    schedule 28.01.2019

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

function defer(obj) {
    obj.promise = new Promise((resolve, reject) => {
        obj.resolve = resolve;
        obj.reject  = reject;
    });
}

Вот упрощенный пример использования этой версии defer() для объединения FontFace load Promise с другим асинхронным процессом:

function onDOMContentLoaded(evt) {
    let all = []; // array of Promises
    glob = {};    // global object used elsewhere
    defer(glob);
    all.push(glob.promise);
    // launch async process with callback = resolveGlob()

    const myFont = new FontFace("myFont", "url(myFont.woff2)");
    document.fonts.add(myFont);
    myFont.load();
    all.push[myFont];
    Promise.all(all).then(() => { runIt(); }, (v) => { alert(v); });
}
//...
function resolveGlob() {
    glob.resolve();
}
function runIt() {} // runs after all promises resolved 

Обновление: 2 альтернативы, если вы хотите инкапсулировать объект:

function defer(obj = {}) {
    obj.promise = new Promise((resolve, reject) => {
        obj.resolve = resolve;
        obj.reject  = reject;
    });
    return obj;
}
let deferred = defer();

а также

class Deferred {
    constructor() {
        this.promise = new Promise((resolve, reject) => {
            this.resolve = resolve;
            this.reject  = reject;
        });
    }
}
let deferred = new Deferred();
person jamess    schedule 11.06.2019
comment
Если вы используете эти примеры в асинхронной функции, вам нужно будет обратиться к свойству обещания, когда вы хотите использовать значение разрешенного обещания: const result = await deferred.promise; - person b00t; 09.05.2020

В некоторых случаях мне также не хватает паттерна Deferred. Вы всегда можете создать его поверх обещания ES6:

export default class Deferred<T> {
    private _resolve: (value: T) => void = () => {};
    private _reject: (value: T) => void = () => {};

    private _promise: Promise<T> = new Promise<T>((resolve, reject) => {
        this._reject = reject;
        this._resolve = resolve;
    })

    public get promise(): Promise<T> {
        return this._promise;
    }

    public resolve(value: T) {
        this._resolve(value);
    }

    public reject(value: T) {
        this._reject(value);
    }
}
person Carsten Hess    schedule 29.10.2019
comment
Мне нравится этот. Я бы просто поменял подпись с отклонения на отклонение (причина: любая) - person Bruno Marotta; 24.02.2021

Наше решение состояло в том, чтобы использовать замыкания для хранения функций разрешения / отклонения и дополнительно прикрепить функцию для расширения самого обещания.

Вот шаблон:

function getPromise() {

    var _resolve, _reject;

    var promise = new Promise((resolve, reject) => {
        _reject = reject;
        _resolve = resolve;
    });

    promise.resolve_ex = (value) => {
       _resolve(value);
    };

    promise.reject_ex = (value) => {
       _reject(value);
    };

    return promise;
}

И используя это:

var promise = getPromise();

promise.then(value => {
    console.info('The promise has been fulfilled: ' + value);
});

promise.resolve_ex('hello');  
// or the reject version 
//promise.reject_ex('goodbye');
person Steven Spungin    schedule 12.01.2017
comment
Отлично ... Я только изучаю обещания, но меня постоянно озадачивает тот факт, что вы, похоже, не можете разрешить их где-то еще. Замыкание, чтобы скрыть детали реализации, - отличная идея ... но на самом деле я не уверен, что вы сделали: вместо того, чтобы использовать псевдоприватные переменные, я почти уверен, что есть способ полностью скрыть переменные, которые должны быть недоступны ... что на самом деле означает закрытие ... - person mike rodent; 03.07.2017
comment
›Замыкание - это блок кода, на который можно ссылаться (и передавать) с доступом к переменным охватывающей области. var _resolve, _reject; являются охватывающей областью. - person Steven Spungin; 03.07.2017
comment
да, достаточно честно. На самом деле мне кажется, что мой ответ слишком усложняет ситуацию, и, кроме того, ваш ответ можно упростить: вам просто нужно пойти promise.resolve_ex = _resolve; promise.reject_ex = _reject; ... все еще работает нормально. - person mike rodent; 04.07.2017
comment
прикрепите функцию для расширения самого обещания. - не делайте этого. Обещания - это значения результата, они не должны обеспечивать возможность их решения. Вы не хотите передавать эти расширенные. - person Bergi; 04.07.2017
comment
Вопрос был в том, как разрешить это вне рамок. Вот решение, которое работает, и в нашем производстве у нас на самом деле была необходимая причина для этого. Я не понимаю, почему решение заявленной проблемы заслуживает отрицательной оценки. - person Steven Spungin; 04.07.2017

Да, ты можешь. Используя CustomEvent API для среды браузера. И используя проект эмиттера событий в средах node.js. Поскольку фрагмент в вопросе предназначен для среды браузера, вот рабочий пример для него.

function myPromiseReturningFunction(){
  return new Promise(resolve => {
    window.addEventListener("myCustomEvent", (event) => {
       resolve(event.detail);
    }) 
  })
}


myPromiseReturningFunction().then(result => {
   alert(result)
})

document.getElementById("p").addEventListener("click", () => {
   window.dispatchEvent(new CustomEvent("myCustomEvent", {detail : "It works!"}))
})
<p id="p"> Click me </p>

Надеюсь, этот ответ будет вам полезен!

person Bhargav Ponnapalli    schedule 18.05.2019

Спасибо всем, кто разместил в этой теме. Я создал модуль, который включает объект Defer (), описанный ранее, а также несколько других построенных на нем объектов. Все они используют Promises и аккуратный синтаксис обратного вызова Promise для реализации взаимодействия / обработки событий в программе.

  • Отложить: обещание, которое может быть разрешено удаленно (вне его тела)
  • Задержка: обещание, которое выполняется автоматически по истечении заданного времени.
  • TimeOut: обещание, которое автоматически завершается ошибкой по прошествии определенного времени.
  • Цикл: повторно запускаемое обещание управлять событиями с синтаксисом обещания
  • Очередь: очередь выполнения на основе цепочки обещаний.

rp = require("openpromise")

https://github.com/CABrouwers/openpromise https://www.npmjs.com/package/openpromise

person CABrouwers    schedule 01.05.2020


Я создал библиотеку под названием manual-promise, которая заменяет Promise. Ни один из других ответов здесь не будет работать как замена для Promise, поскольку они используют прокси или оболочки.

yarn add manual-promise

npn install manual-promise


import { ManualPromise } from "manual-promise";

const prom = new ManualPromise();

prom.resolve(2);

// actions can still be run inside the promise
const prom2 = new ManualPromise((resolve, reject) => {
    // ... code
});


new ManualPromise() instanceof Promise === true

https://github.com/zpxp/manual-promise#readme

person jeohd    schedule 03.08.2019

Как насчет создания функции для перехвата отклонения и его возврата?

function createRejectablePromise(handler) {
  let _reject;

  const promise = new Promise((resolve, reject) => {
    _reject = reject;

    handler(resolve, reject);
  })

  promise.reject = _reject;
  return promise;
}

// Usage
const { reject } = createRejectablePromise((resolve) => {
  setTimeout(() => {
    console.log('resolved')
    resolve();
  }, 2000)

});

reject();
person nikksan    schedule 23.03.2019

Я собрал суть, которая выполняет эту работу: https://gist.github.com/thiagoh/c24310b562d50a14f3e7602a82b4ef13

вот как вы должны его использовать:

import ExternalizedPromiseCreator from '../externalized-promise';

describe('ExternalizedPromise', () => {
  let fn: jest.Mock;
  let deferredFn: jest.Mock;
  let neverCalledFn: jest.Mock;
  beforeEach(() => {
    fn = jest.fn();
    deferredFn = jest.fn();
    neverCalledFn = jest.fn();
  });

  it('resolve should resolve the promise', done => {
    const externalizedPromise = ExternalizedPromiseCreator.create(() => fn());

    externalizedPromise
      .promise
      .then(() => deferredFn())
      .catch(() => neverCalledFn())
      .then(() => {
        expect(deferredFn).toHaveBeenCalled();
        expect(neverCalledFn).not.toHaveBeenCalled();
        done();
      });

    expect(fn).toHaveBeenCalled();
    expect(neverCalledFn).not.toHaveBeenCalled();
    expect(deferredFn).not.toHaveBeenCalled();

    externalizedPromise.resolve();
  });
  ...
});
person thiagoh    schedule 27.05.2019

Еще одно решение для разрешения Promise извне

 class Lock {
        #lock;  // Promise to be resolved (on  release)
        release;  // Release lock
        id;  // Id of lock
        constructor(id) {
            this.id = id
            this.#lock = new Promise((resolve) => {
                this.release = () => {
                    if (resolve) {
                        resolve()
                    } else {
                        Promise.resolve()
                    }
                }
            })
        }
        get() { return this.#lock }
    }

использование

let lock = new Lock(... some id ...);
...
lock.get().then(()=>{console.log('resolved/released')})
lock.release()  // Excpected 'resolved/released'
person Egor Cherniaev    schedule 12.05.2020

Так как я не нашел то, что искал, я поделюсь тем, чего действительно хотел достичь, когда закончил с этим вопросом.

Сценарий: у меня есть 3 разных API с одинаковым возможным ответом, поэтому я хотел бы обрабатывать завершение и обработку ошибок обещаний в одной функции. Вот что я сделал:

  1. Создайте функцию-обработчик:
  private handleHttpPromise = (promise: Promise<any>) => {
    promise
      .then((response: any) => {
        // do something with the response
        console.log(response);
      })
      .catch((error) => {
        // do something with the error
        console.log(error);
      });
  };
  1. Отправьте свои обещания созданному обработчику
  switch (method) {
    case 'get': {
      this.handleHttpPromise(apiService.get(url));
      break;
    }
    case 'post': {
      if (jsonData) {
        this.handleHttpPromise(apiService.post(url, jsonData));
      }
      break;
    }
    // (...)
  }
person Daniel Santana    schedule 28.11.2020

Хочу поделиться чем-нибудь другим, продолжением этой темы.

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

Пример повторяющегося обещания с внешним преобразователем. Каждый раз, когда вызывается преобразователь, создается новое обещание по тому же адресу / переменной / свойству.

let resolvePromise;
let thePromise;

const setPromise = (resolve) => {
  resolvePromise = () => {
    resolve();
    thePromise = new Promise(setPromise);   
  }
}
thePromise = new Promise(setPromise);

(async () => {
  let i = 0;
  while (true) {
    let msg = (i % 2 === 0) ? 'Tick' : 'Tock';
    document.body.innerHTML = msg;
    setTimeout(resolvePromise, 1000);
    await thePromise;
    i++;
  }
})();

https://jsfiddle.net/h3zvw5xr

person Maciej Krawczyk    schedule 25.02.2021

сначала включите синтаксис --allow-natives-в браузере или на узле

const p = new Promise(function(resolve, reject){
    if (someCondition){
        resolve();
    } else {
        reject();
    } 
});

onClick = function () {
    %ResolvePromise(p, value)
}

person Equitable    schedule 26.07.2019