[javascript] Resolve Javascript Promise outside function scope

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?

This question is related to javascript promise es6-promise

The answer is


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)

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

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'

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

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

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

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

simple:

var promiseResolve, promiseReject;

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

promiseResolve();

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


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


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.

_x000D_
_x000D_
function myPromiseReturningFunction(){_x000D_
  return new Promise(resolve => {_x000D_
    window.addEventListener("myCustomEvent", (event) => {_x000D_
       resolve(event.detail);_x000D_
    }) _x000D_
  })_x000D_
}_x000D_
_x000D_
_x000D_
myPromiseReturningFunction().then(result => {_x000D_
   alert(result)_x000D_
})_x000D_
_x000D_
document.getElementById("p").addEventListener("click", () => {_x000D_
   window.dispatchEvent(new CustomEvent("myCustomEvent", {detail : "It works!"}))_x000D_
})
_x000D_
<p id="p"> Click me </p>
_x000D_
_x000D_
_x000D_

I hope this answer is useful!


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.

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

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

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.

_x000D_
_x000D_
class DeferredPromise {_x000D_
  constructor() {_x000D_
    this._promise = new Promise((resolve, reject) => {_x000D_
      // assign the resolve and reject functions to `this`_x000D_
      // making them usable on the class instance_x000D_
      this.resolve = resolve;_x000D_
      this.reject = reject;_x000D_
    });_x000D_
    // bind `then` and `catch` to implement the same interface as Promise_x000D_
    this.then = this._promise.then.bind(this._promise);_x000D_
    this.catch = this._promise.catch.bind(this._promise);_x000D_
    this[Symbol.toStringTag] = 'Promise';_x000D_
  }_x000D_
}_x000D_
_x000D_
const deferred = new DeferredPromise();_x000D_
console.log('waiting 2 seconds...');_x000D_
setTimeout(() => {_x000D_
  deferred.resolve('whoa!');_x000D_
}, 2000);_x000D_
_x000D_
async function someAsyncFunction() {_x000D_
  const value = await deferred;_x000D_
  console.log(value);_x000D_
}_x000D_
_x000D_
someAsyncFunction();
_x000D_
_x000D_
_x000D_


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


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

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

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


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

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 -

_x000D_
_x000D_
function flatPromise() {_x000D_
_x000D_
    let resolve, reject;_x000D_
_x000D_
    const promise = new Promise((res, rej) => {_x000D_
        resolve = res;_x000D_
        reject = rej;_x000D_
    });_x000D_
_x000D_
    return { promise, resolve, reject };_x000D_
}_x000D_
_x000D_
function doSomethingAsync() {_x000D_
    _x000D_
    // Get your promise and callbacks_x000D_
    const { resolve, reject, promise } = flatPromise();_x000D_
_x000D_
    // Do something amazing..._x000D_
    setTimeout(() => {_x000D_
        resolve('done!');_x000D_
    }, 500);_x000D_
_x000D_
    // Pass your promise to the world_x000D_
    return promise;_x000D_
}_x000D_
_x000D_
(async function run() {_x000D_
_x000D_
    const result = await doSomethingAsync()_x000D_
        .catch(err => console.error('rejected with', err));_x000D_
    console.log(result);_x000D_
_x000D_
})();
_x000D_
_x000D_
_x000D_

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


Examples related to javascript

need to add a class to an element How to make a variable accessible outside a function? Hide Signs that Meteor.js was Used How to create a showdown.js markdown extension Please help me convert this script to a simple image slider Highlight Anchor Links when user manually scrolls? Summing radio input values How to execute an action before close metro app WinJS javascript, for loop defines a dynamic variable name Getting all files in directory with ajax

Examples related to promise

Axios handling errors typescript: error TS2693: 'Promise' only refers to a type, but is being used as a value here Syntax for async arrow function Angular 2: How to call a function after get a response from subscribe http.post How to use fetch in typescript Returning Promises from Vuex actions Use async await with Array.map Getting a UnhandledPromiseRejectionWarning when testing using mocha/chai using setTimeout on promise chain Why is my asynchronous function returning Promise { <pending> } instead of a value?

Examples related to es6-promise

How to reject in async/await syntax? What is difference between Axios and Fetch? What is an unhandled promise rejection? JavaScript ES6 promise for loop Returning Promises from Vuex actions how to cancel/abort ajax request in axios Axios get access to response header fields How to pass parameter to a promise function Wait until all promises complete even if some rejected Handling errors in Promise.all