Resolve Javascript Promise outside the Promise constructor scope

JavascriptPromiseEs6 Promise

Javascript Problem Overview


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?

Javascript Solutions


Solution 1 - Javascript

simple:

var promiseResolve, promiseReject;

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

promiseResolve();

Solution 2 - Javascript

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

Solution 3 - Javascript

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"]();

Solution 4 - Javascript

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: > > > js > 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();

Solution 5 - Javascript

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)

Solution 6 - Javascript

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

Solution 7 - Javascript

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

Solution 8 - Javascript

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.

Solution 9 - Javascript

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.

Solution 10 - Javascript

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

Solution 11 - Javascript

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

Solution 12 - Javascript

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

Solution 13 - Javascript

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

Solution 14 - Javascript

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!

Solution 15 - Javascript

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

Solution 16 - Javascript

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

Solution 17 - Javascript

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

Solution 18 - Javascript

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'

Solution 19 - Javascript

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

Solution 20 - Javascript

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

Solution 21 - Javascript

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();
  });
  ...
});

Solution 22 - Javascript

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;
    }
    // (...)
  }

Solution 23 - Javascript

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

Solution 24 - Javascript

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMorioView Question on Stackoverflow
Solution 1 - JavascriptcarterView Answer on Stackoverflow
Solution 2 - JavascriptJon JaquesView Answer on Stackoverflow
Solution 3 - JavascriptBenjamin GruenbaumView Answer on Stackoverflow
Solution 4 - JavascriptRico KahlerView Answer on Stackoverflow
Solution 5 - JavascriptMaxmaxmaximusView Answer on Stackoverflow
Solution 6 - JavascriptAliView Answer on Stackoverflow
Solution 7 - JavascriptCory DanielsonView Answer on Stackoverflow
Solution 8 - JavascriptArikView Answer on Stackoverflow
Solution 9 - JavascriptHinrichView Answer on Stackoverflow
Solution 10 - JavascriptjamessView Answer on Stackoverflow
Solution 11 - JavascriptCarsten HessView Answer on Stackoverflow
Solution 12 - JavascriptMichal FilipView Answer on Stackoverflow
Solution 13 - JavascriptSteven SpunginView Answer on Stackoverflow
Solution 14 - JavascriptBhargav PonnapalliView Answer on Stackoverflow
Solution 15 - JavascriptCABrouwersView Answer on Stackoverflow
Solution 16 - Javascriptinf3rnoView Answer on Stackoverflow
Solution 17 - JavascriptjeohdView Answer on Stackoverflow
Solution 18 - JavascriptEgor CherniaevView Answer on Stackoverflow
Solution 19 - JavascriptJerem LachkarView Answer on Stackoverflow
Solution 20 - JavascriptnikksanView Answer on Stackoverflow
Solution 21 - JavascriptthiagohView Answer on Stackoverflow
Solution 22 - JavascriptDaniel SantanaView Answer on Stackoverflow
Solution 23 - JavascriptMaciej KrawczykView Answer on Stackoverflow
Solution 24 - JavascriptEquitableView Answer on Stackoverflow