500

I have been using ES6 Promise.

Ordinarily, a Promise is constructed and used like this

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

But I have been doing something like below to take the resolve outside for the sake of flexibility.

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

And later

onClick = function(){
    outsideResolve();
}

This works fine, but is there an easier way to do this? If not, is this a good practice?

mPrinC
  • 9,147
  • 2
  • 32
  • 31
Morio
  • 8,463
  • 5
  • 25
  • 29
  • 2
    I don't think there is another way. I believe it is specified that the callback passed to `Promise` has to be executed synchronously to allow "exporting" the two functions. – Felix Kling Oct 01 '14 at 20:44
  • 1
    This works for me exactly like you wrote it. So as far as I'm concerned, this is the "canonical" way. – Gilad Barner Jul 31 '16 at 08:40
  • 44
    I think there should be a formal way to achieve this in the future. This feature is very powerful in my opinion as you can wait for values from other contexts. – Jose Apr 03 '18 at 12:47
  • Whenever they come up with a proper solution to this problem, I hope they will also make it work for nested promises, some of which may recur. – Arthur Tarasov Jul 05 '18 at 08:02
  • 1
    I think the Promise API "suggest" to always use them as return values and never as objects that you can access or call. In other words force us to treat them as return values instead of objects we can access or functions we can call or something we can reference with a variable or pass as a parameter, etc. If you start using promises as any other object probably you will end up needing to resolve it from outside like in your question... That being said, I also think there should should be a formal way of doing this... and Deferred seems just a workaround for me. – cancerbero May 14 '19 at 07:44
  • @Jose For me it's been useful a couple times, when I need a Promise to resolve immediately after another promise, in cases where Promise.all or Promise.race don't work. – JulianSoto Mar 16 '21 at 21:51
  • This pattern will cause a lot of unexpected issues when things start to get complex. Probably a good alternative is to use events in these cases. – Pato Mar 11 '22 at 19:46

27 Answers27

258

simple:

var promiseResolve, promiseReject;

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

promiseResolve();
carter
  • 5,074
  • 4
  • 31
  • 40
  • 3
    @ruX, As the accepted answer mentions - it was designed this way on purpose. The point is that if an exception is thrown it will be caught by the promise constructor. This answer (as well as mine) has the pitfall of possibly throwing an exception for whatever code calls `promiseResolve()`. The semantics of a promise are that it *always* returns a value. Also this is functionally the same as OP's post, I don't get what problem this is solving in a reusable way. – Jon Jaques Jun 27 '16 at 20:30
  • 5
    @JonJaques I'm not sure if what you say is true. The code that calls `promiseResolve()` will not throw an exception. You can define a `.catch` on the constructor and no matter what code calls it, the constructor's `.catch` will be called. Here is the jsbin demonstrating how this works: https://jsbin.com/yicerewivo/edit?js,console – carter Jul 05 '16 at 22:26
  • 3
    Yeah, it's caught because you wrapped another promise constructor around it - Exactly the point I'm trying to make. However, lets say you have some other code that's trying to call resolve() outside of the constructor (aka Deferred object)... It could throw an exception and not be caught https://jsbin.com/cokiqiwapo/1/edit?js,console – Jon Jaques Jul 06 '16 at 13:26
  • 1
    I see what you mean now, but I don't see how that relates to creating flexible promise resolvers. If you throw an error outside of something that can catch it, then it doesn't get caught. This is not a pitfall of a promise pattern. This is just an example of a bad pattern. – carter Jul 12 '16 at 17:17
  • 13
    I am not even sure it is a bad design. An error thrown outside the promise isn't supposed to be caught within the promise. It is perhaps an example of misconception or bad understanding, if the designer actually _expects_ the error to be caught within. – KalEl May 11 '17 at 22:17
  • 49
    This exact construct is already mentioned in the question. Did you even read it? – Cedric Reichenbach Sep 30 '17 at 15:08
  • 1
    @carter ,What if the promise was created by `(async()=>{})()`? – Pacerier Oct 16 '17 at 01:33
  • I love the simplicity – ChrisRich Jun 30 '22 at 07:13
158

Bit late to the party here, but another way to do it would be to use a Deferred object. You essentially have the same amount of boilerplate, but it's handy if you want to pass them around and possibly resolve outside of their definition.

Naive Implementation:

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 Version:

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
})
Jon Jaques
  • 4,262
  • 2
  • 23
  • 25
  • 1
    Do notice the lexical scoping here. – Nebula Feb 02 '16 at 20:45
  • 1
    There is no practical difference in whether `resolve|reject` are assigned lexically or through `bind`. This is just a simple implementation of the [jQuery Deferred](https://api.jquery.com/jquery.deferred/) object that has been around since 1.0(ish). It works exactly like a promise, except there is no throw safety. The whole point of this question was how to save a few lines of code when creating promises. – Jon Jaques Jul 13 '16 at 00:08
  • 4
    Using a deferred is the usual way to do this, I have no idea why this isn't higher – BlueRaja - Danny Pflughoeft Feb 28 '17 at 22:21
  • 2
    Excellent answer! Was looking for the deferred functionality that jQuery offers. – Anshuul Kai Mar 28 '17 at 22:16
  • 2
    Is `Deferred` deprecated? – Pacerier Oct 16 '17 at 01:34
  • @Pacerier in jQuery? probably. This is a clean implementation on top of modern apis though. As you can see there's not much to it. – Jon Jaques Jan 19 '18 at 20:07
  • 1
    I don't know if this is accepted nowdays or not, but it works very well, so I'm fine with it. No problems so far. – beatcoder Dec 25 '19 at 07:26
  • Accepted? meh. Just be aware that if the asyncAction function (thing which creates the deferred object) throws an except during creation, you will not get a promise back. Just be careful about what you're doing ;) – Jon Jaques Jan 16 '20 at 21:49
  • jQuery was ahead of its time on this lmao – Jerem Lachkar Feb 17 '22 at 13:38
  • @JonJaques why "naive"? this looks really nice and simple. I don't think this is incomplete in any way, is it? if so, it would be good to elaborate on why you think this approach is "naive" :-) – mindplay.dk Sep 01 '23 at 08:53
126

No, there is no other way to do this - the only thing I can say is that this use case isn't very common. Like Felix said in the comment - what you do will consistently work.

It's worth mentioning that the reason the promise constructor behaves this way is throw safety - if an exception you did not anticipate happens while your code is running inside the promise constructor it will turn into a rejection, this form of throw safety - converting thrown errors to rejections is important and helps maintain predictable code.

For this throw safety reason, the promise constructor was chosen over deferreds (which are an alternative promise construction way that do allow what you're doing) - as for best practices - I'd pass the element and use the promise constructor instead:

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

For this reason - whenever you can use the promise constructor over exporting the functions - I recommend you do use it. Whenever you can avoid both - avoid both and chain.

Note, that you should never use the promise constructor for things like if(condition), the first example could be written as:

var p = Promise[(someCondition)?"resolve":"reject"]();
Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504
  • 2
    Hi Benjamin! Is there currently no better way of getting yummy promise sugar if we don't know when the promise will be fulfilled yet? Like some sort of asynchronous [wait/notify pattern](http://www.programcreek.com/2009/02/notify-and-wait-example/)? Like for example, "store", and later invoke a `Promise` chain? E.g. in my particular case, I am on a server, waiting for a specific client reply (a SYN-ACK-kinda hand-shake to make sure the client successfully updated state). – Domi May 03 '15 at 13:17
  • 1
    @Domi check out q-connection and RxJS. – Benjamin Gruenbaum May 03 '15 at 14:09
  • Hah, as usual, I was too impatient to wait! Either way, it seems like what those libraries are doing is overkill for my particular use-case, unless they are fixing issues that I have overlooked. Mind taking a look at [my minimal solution](http://stackoverflow.com/a/30014830/2228771)? – Domi May 03 '15 at 14:17
  • 2
    How could do I the same using fetch API? – Vinod Sobale Apr 20 '17 at 05:05
  • 154
    Not common? I end up needing it almost every project. – Tomáš Zato Jun 23 '17 at 15:24
  • 1
    @TomášZato interesting, I would love to learn about some use cases for this - if you could create some samples on a [gist](https://gist.github.com) I will definitely take a look and forward your concerns to the technical committee. – Benjamin Gruenbaum Jun 23 '17 at 15:49
  • I do not see how this last line of code could be considered a good way of writing code. This is very hard to read. I would go for verbose readable way as OP did. – Gherman Oct 24 '17 at 08:26
  • 2
    As for the usecase consider you need to do something after an event is triggered and something else happened. You want to transform event into a promise and unite it with another promise. Seems like a generic problem to me. – Gherman Oct 24 '17 at 08:31
  • 1
    Seems like Promise API was designed to prevent calling resolve() from outside the constructor. But is not uncommon to have more than one "actor" that collaborate to define a async API, one that knows when to install the promise and another that knows how to resolve it. And I don't want to add an emitter in the middle just for this, and the design is not forbidding me to hack it, so, I wonder what would be the correct pattern to use here. Would it be Deferred class as suggested below? Should I use an event emitter like in the old times and loose async/await syntax ? Any ideas? – cancerbero May 14 '19 at 07:28
  • 1
    @cancerbero the promise API was designed to be used this way because if the function passed to `new Promise` throws you get a rejected promise. It is also explicitly synchronous and you can rely on the deferred parttern. – Benjamin Gruenbaum May 14 '19 at 09:37
  • 1
    @BenjaminGruenbaum - My first use case for this was waiting for fonts to load combined with other async processes. It allows me to create one callback for the completion of specific combinations of fonts and other code. I was doing this with 2 global ints: `totalCount` and `loadedCount`. I would `++loadedCount` in `document.fonts.ready.then()` and in event handlers for async processes. Now I don't have to keep track of how many processes are in each group. Beyond font loading, I now use it to create a single callback for multiple AJAX requests. I added an answer with a fonts example. – Sideways S Jun 11 '19 at 16:11
  • p-defer worked for me: https://www.npmjs.com/package/p-defer after this solution worked and I thought maybe there would be something already built out there. – Chris Aug 13 '19 at 22:03
  • `this.onclick = resolve;` will cause calling `resolve` function in the promise on every click which might be an overhead since a promise can be resolved only once. Doesn't hurt but also not the most efficient solution. Especially if you are doing it this way was to catch eventual errors which, on this line, will not occur either way. – Hrvoje Golcic Jun 21 '20 at 10:22
  • Arguably writing `var p = Promise[(someCondition)?"resolve":"reject"]();` might lead to less readable and maintainable code. Of course it depends always on the specific use case but really no need to be fancy here. Such code was written here for example, of course it makes no sense to make a promise that works that way. – Hrvoje Golcic Jun 21 '20 at 10:28
  • 2
    @BenjaminGruenbaum - Another use case is if you are communicating with a web worker. If you are expecting multiple pieces of info to arrive via web worker mesages (in an unknown order), it is nice to make a promise p[i] for each piece of info, so consumers of that info could await that promise or register a callback via p[i].then(callme). This promise needs to be resolved by worker.onmessage, not by code provided at promise creation. (Or rejected by code in worker.onerror.) Basically any time an asynchronous process triggers multiple unordered callbacks, you want what the OP is talking about. – Matt Mar 18 '21 at 12:01
  • 6
    It would be so handy if you could just `var p = new Promise(); p.resolve()` – br4nnigan Apr 14 '21 at 08:30
  • @br4nnigan This is both an invalid syntax (you must pass a resolver function) and not supported API (resolve is not exposed nor exist in the returned object). – Sagiv b.g Feb 19 '23 at 15:46
38

I liked @JonJaques answer but I wanted to take it a step further.

If you bind then and catch then the Deferred object, then it fully implements the Promise API and you can treat it as promise and await it and such.

⚠️ Editor's Note: I don't recommend this kind of pattern anymore since at the time of writing, Promise.prototype.finally was not a thing yet, then it became a thing… This could happen to other methods so I recommend you augment the promise instance with resolve and reject functions instead:

function createDeferredPromise() {
  let resolve
  let reject

  const promise = new Promise((thisResolve, thisReject) => {
    resolve = thisResolve
    reject = thisReject
  })

  return Object.assign(promise, {resolve, reject})
}

Go upvote someone else's answer.

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.finally = this._promise.finally.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();
Rico Kahler
  • 17,616
  • 11
  • 59
  • 85
  • I really love this. Thank you. I'm using it as a custom defined component in my Express app, but it'd be great as an NPM module if you were willing to create one, or I could if needed. This approach is a great mashup of the new async / await and how the old Parse Platform used to approach promises https://en.wikipedia.org/wiki/Parse_(platform) – Michael Kubler Aug 02 '20 at 18:44
  • 1
    Don’t forget the `Promise.prototype.finally`. – Константин Ван Nov 08 '21 at 17:31
  • Good catch @КонстантинВан, I haven't seen this answer in a minute and I don't recommend this anymore. I've updated the answer to reflect – Rico Kahler Nov 08 '21 at 20:31
  • Well, if you’re worrying about possible future changes in the methods of `Promise`, you could also generalize the mapping work by looping through the properties of `Promise`, no? – Константин Ван Nov 08 '21 at 20:50
30

A solution I came up with in 2015 for my framework. I called this type of promises Task

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)
Maxmaxmaximus
  • 2,098
  • 1
  • 19
  • 17
  • 5
    Thanks, this worked. But what is handler? I had to remove it to get it working. – Sahid Feb 01 '19 at 05:52
  • @Sahid when you run createPromise() you need to pass a function as argument to it. otherwise the code does not work. You could have an if statement and check for the validity of the handler argument before calling it. – Michael Mammoliti Sep 15 '20 at 18:22
  • Thanks for the code! But isn't it possible for some other code to call your `.resolve` before the callback sets it? I'm used to regular threads, not asynchronous events, so I might be a bit confused. – adentinger Oct 29 '20 at 01:22
22

Accepted answer is wrong. It's pretty easy using scope and references, though it may make Promise purists angry:

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);

We are essentially grabbing the reference to the resolve function when the promise is created, and we return that so it can be set externally.

In one second the console will output:

> foo
Ali
  • 2,439
  • 23
  • 13
  • I think this is the best approach. The only thing is that the code could be a bit less verbose. – Adam Pietrasiak Sep 12 '19 at 11:52
  • Nice! Clever idea. +50 if I could. – Mitya Mar 12 '20 at 14:59
  • This is just what OP did. In fact you are re-inventing Deferred pattern over Promises, of course this is possible and your approach works (as the initial OP code), but this is not the best practice due to "throw safety reason" described in the accepted answer. – dhilt May 21 '20 at 02:37
15

A helper method would alleviate this extra overhead, and give you the same jQuery feel.

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

Usage would be

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

Which is similar to jQuery

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

Although, in a use case this simple, native syntax is fine

return new Promise((resolve, reject) => {
    displayConfirmationDialog({
        confirm: resolve,
        cancel: reject
    });
});
Cory Danielson
  • 14,314
  • 3
  • 44
  • 51
13

I'm using a helper function to create what I call a "flat promise" -

function flatPromise() {

    let resolve, reject;

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

    return { promise, resolve, reject };
}

And I'm using it like so -

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;

}

See full working example -

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);

})();

Edit: I have created an NPM package called flat-promise and the code is also available on GitHub.

Arik
  • 5,266
  • 1
  • 27
  • 26
13

You can wrap the Promise in a class.

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.
Hinrich
  • 13,485
  • 7
  • 43
  • 66
13

Just in case somebody came looking for a typescript version of a util simplifying this task:

export const deferred = <T>() => {
  let resolve!: (value: T | PromiseLike<T>) => void;
  let reject!: (reason?: any) => void;
  const promise = new Promise<T>((res, rej) => {
    resolve = res;
    reject = rej;
  });

  return {
    resolve,
    reject,
    promise,
  };
};

This can be used eg. like:

const {promise, resolve} = deferred<string>();

promise.then((value) => console.log(value)); // nothing

resolve('foo'); // console.log: foo

Michal Filip
  • 870
  • 8
  • 9
11

I find myself missing the Deferred pattern as well in certain cases. You can always create one on top of a ES6 Promise:

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);
    }
}
Carsten Hess
  • 1,794
  • 1
  • 12
  • 5
9

Many of the answers here are similar to the last example in this article. I am caching multiple Promises, and the resolve() and reject() functions can be assigned to any variable or property. As a result I am able to make this code slightly more compact:

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

Here is a simplified example of using this version of defer() to combine a FontFace load Promise with another async process:

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 

Update: 2 alternatives in case you want to encapsulate the object:

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

and

class Deferred {
    constructor() {
        this.promise = new Promise((resolve, reject) => {
            this.resolve = resolve;
            this.reject  = reject;
        });
    }
}
let deferred = new Deferred();
Sideways S
  • 601
  • 7
  • 12
  • If you're using these examples in an async function, you'll need to refer to the promise property, when you want to use the value of the resolved promise: `const result = await deferred.promise;` – b00t May 09 '20 at 05:42
5

Our solution was to use closures to store the resolve/reject functions and additionally attach a function to extend the promise itself.

Here is the pattern:

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;
}

And using it:

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');
Steven Spungin
  • 27,002
  • 5
  • 88
  • 78
  • 3
    Great... I'm just learning Promises but have been consistently puzzled by the fact that you don't appear to be able to resolve them "somewhere else". Using a closure to hide implementation details is a great idea... but in fact I'm not sure that's what you've done: rather than have "pseudo" private variables I'm pretty sure there's a way to *completely* conceal the variables which should be inaccessible... which is really what closures mean... – mike rodent Jul 03 '17 at 18:05
  • > A closure is a block of code that can be referenced (and passed around) with access to the variables of the enclosing scope. var _resolve, _reject; are the enclosing scope. – Steven Spungin Jul 03 '17 at 20:12
  • yep, fair enough. Actually it seems to me that my answer is overcomplicating things, and furthermore that your answer can be simplified: you just need to go `promise.resolve_ex = _resolve; promise.reject_ex = _reject;` ... still works fine. – mike rodent Jul 03 '17 at 21:03
  • "*attach a function to extend the promise itself.*" - don't do that. Promises are result values, they should not provide the capability to resolve them. You don't want to pass those extended ones around. – Bergi Jul 04 '17 at 16:41
  • 2
    The question was how to resolve it outside of the scope. Here is a solution that works, and in our production we have actually had a necessary reason to do it. I don't see why solving the problem stated deserves a downvote. – Steven Spungin Jul 04 '17 at 20:46
3

Yes, you can. By using the CustomEvent API for the browser environment. And using an event emitter project in node.js environments. Since the snippet in the question is for the browser environment, here is a working example for the same.

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>

I hope this answer is useful!

Bhargav Ponnapalli
  • 9,224
  • 7
  • 36
  • 45
3

Thanks to everyone who posted in this thread. I created a module that includes the Defer() object described earlier as well as a few other objects built upon it. They all leverage Promises and the neat Promise call-back syntax to implement communication/event handling within a program.

  • Defer: Promise that can be resolved failed remotely (outside of its body)
  • Delay: Promise that is resolved automatically after a given time
  • TimeOut: Promise that fails automatically after a given time.
  • Cycle: Re-triggerable promise to manage events with the Promise syntax
  • Queue: Execution queue based on Promise chaining.

rp = require("openpromise")

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

CABrouwers
  • 33
  • 2
3

Class version, in Typescript :

export class Deferred<T> {
    public readonly promise: Promise<T>
    private resolveFn!: (value: T | PromiseLike<T>) => void
    private rejectFn!: (reason?: any) => void

    public constructor() {
        this.promise = new Promise<T>((resolve, reject) => {
            this.resolveFn = resolve
            this.rejectFn = reject
        })
    }

    public reject(reason?: any): void {
        this.rejectFn(reason)
    }

    public resolve(param: T): void {
        this.resolveFn(param)
    }
}
Jerem Lachkar
  • 1,008
  • 11
  • 24
1

I wrote a small lib for this. https://www.npmjs.com/package/@inf3rno/promise.exposed

I used the factory method approach others wrote, but I overrode the then, catch, finally methods too, so you can resolve the original promise by those as well.

Resolving Promise without executor from outside:

const promise = Promise.exposed().then(console.log);
promise.resolve("This should show up in the console.");

Racing with the executor's setTimeout from outside:

const promise = Promise.exposed(function (resolve, reject){
    setTimeout(function (){
        resolve("I almost fell asleep.")
    }, 100000);
}).then(console.log);

setTimeout(function (){
    promise.resolve("I don't want to wait that much.");
}, 100);

There is a no-conflict mode if you don't want to pollute the global namespace:

const createExposedPromise = require("@inf3rno/promise.exposed/noConflict");
const promise = createExposedPromise().then(console.log);
promise.resolve("This should show up in the console.");
inf3rno
  • 24,976
  • 11
  • 115
  • 197
1

I made a library called manual-promise that functions as a drop in replacement for Promise. None of the other answers here will work as drop in replacements for Promise, as they use proxies or wrappers.

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

jeohd
  • 309
  • 1
  • 2
  • 11
1

Just another solution to resolve Promise from the outside

 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 }
    }

Usage

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

If (like me) you don't like augmenting native instances, nor unwieldy ".promise" properties ... but do love proxies and mangling classes, then this one is for you:

class GroovyPromise {
  constructor() {
    return new Proxy(new Promise((resolve, reject) => {
      this.resolve = resolve;
      this.reject = reject;
    }), {
      get: (target, prop) =>
        this[prop] || target[prop].bind(target),
    });
  }
}

Used like so:

const groovypromise = new GroovyPromise();
setTimeout(() => groovypromise.resolve('groovy'), 1000);
console.log(await groovypromise);

Of course you can also rename the class to something dull like "Deferred"

som
  • 2,023
  • 30
  • 37
0

How about creating a function to hijack the reject and return it ?

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();
nikksan
  • 3,341
  • 3
  • 22
  • 27
0

I've put together a gist that does that job: https://gist.github.com/thiagoh/c24310b562d50a14f3e7602a82b4ef13

here's how you should use it:

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();
  });
  ...
});
thiagoh
  • 7,098
  • 8
  • 51
  • 77
0

As I didn't find what I was looking for, I will share what I actually wanted to achieve when I ended in this question.

Scenario: I have 3 different API's with same possible response and therefore I would like to handle the completion and error handling of the promises in a single function. This is what I did:

  1. Create a handler function:
  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. Send your promises to the created handler
  switch (method) {
    case 'get': {
      this.handleHttpPromise(apiService.get(url));
      break;
    }
    case 'post': {
      if (jsonData) {
        this.handleHttpPromise(apiService.post(url, jsonData));
      }
      break;
    }
    // (...)
  }
Dharman
  • 30,962
  • 25
  • 85
  • 135
Daniel Santana
  • 1,493
  • 20
  • 19
0

I would like to share something different, an extension to this topic.

Sometimes you want a "task promise" to be automatically re-created at the same address (property or variable) when it resolves. It's possible to create an outside resolver that does just that.

Example of a recurring promise with an external resolver. Whenever the resolver is called, a new promise is created at the same address/variable/property.

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

Maciej Krawczyk
  • 14,825
  • 5
  • 55
  • 67
0

For fun, you also combine a promise into a self-resolvable function:

function Resolver() {
  let resolve;
  const promise = new Promise(r => resolve = r);
  return new Proxy(resolve, {
    get: (_, prop) => promise[prop].bind(promise)
  });
}

const resolve = Resolver();

(async () => {
  resolve
    .then(value => console.log('thenable:', value))
    .finally(() => console.log('finally'));
  const value = await resolve;
  console.log('awaitable:', value);
})()

resolve('test');
// thenable: test
// finally
// awaitable: test
Spencer
  • 878
  • 8
  • 5
0

If you're not against importing libraries, a very lightweight dependency you can use is p-defer: https://github.com/sindresorhus/p-defer

(With all these answers talking about deferred promises I'm surprised that not a single one mentions the awesome collection of promise modules by sindresorhus on GitHub / npm: https://github.com/sindresorhus/promise-fun)

Isti115
  • 2,418
  • 3
  • 29
  • 35
-5

first enable --allow-natives-syntax on browser or node

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

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

Equitable
  • 37
  • 5