Access resolve function outside of a JavaScript Promise I have an object that needs to have a single point of initialization, and other callers need to wait until the initialization is complete before continuing.

I have done this with jQuery, and it feels like I'm working with the design, but the same approach with a native JavaScript Promise seems like it is going against the grain.

I need to replace use of jQuery promise with a native JS Promise.  

Is this initialization pattern OK, or is there a better approach?

On the surface it seems that an event pub/sub pattern would be a better fit, but I can't guarantee that the init() will be called before the ready() is called, which would result in missed events.

jQuery

function Foo() {

    var dfd= $.Deferred();
    this.ready = function () {
        return dfd.promise();
    }
    this.init = function(){
        setTimeout(function(){
            dfd.resolve();
        }, 2000);
    }
};

var foo = new Foo();

//foo.ready() may be called from multiple places.
foo.ready().then(function () {
    alert('foo.ready');
});

//foo.init would only be called once.
foo.init();


JavaScript Promise

function Foo() {

    var resolver,
        rejector,
        readyPromise = new Promise(function (resolve, reject) {
            resolver = resolve;
            rejector = reject;
        });

    this.ready = function () {
        return readyPromise;
    }
    this.init = function () {
        setTimeout(function () {
            resolver();
        }, 2000);
    }
};

var foo = new Foo();

//foo.ready() may be called from multiple places.
foo.ready().then(function () {
    alert('foo.ready');
});

//foo.init would only be called once.
foo.init();
If I understand your question correctly, your question runs on these assumptions:


You want to hook up multiple listeners to a single Promise.
The promise is triggered arbitrarily, not necessarily on promise creation.
You want to use native promises.
You run on an engine that has Promises.


The second bullet point, where the operation is triggered arbitrarily, is the tricky part because native promises are read-only. You'll need to catch resolve and reject from the constructor every time.

So while your implementation is correct, you'll find yourself repeating the pattern. jQuery Deferreds are better in this case, but we don't want to lug around jQuery, or any library for that matter.

What you could do is create your own little Deferred constructor. It just wraps around the existing native Promise API, essentially just forwarding arguments. The only difference is that it's exposing resolve and reject.

function Deferred(callback){
  var instance = this;

  // Catch the resolve and reject
  this._resolver = null;
  this._rejector = null;
  this._promise = new Promise(function(resolve, reject){
    instance._resolver = resolve;
    instance._rejector = reject;
  });

  // Deferred has { resolve, reject }. But personally, I like the Promise
  // version of resolve and reject as separate args.
  if(typeof callback === 'function')
    callback.call(this, this._resolver, this._rejector);
}

Deferred.prototype.then = function(resolve, reject){
  return this._promise.then(resolve, reject);
};

// resolve, reject etc.

// For other APIs, refer to jQuery for Deferred and MDN for Promises:
// https://api.jquery.com/category/deferred-object/
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise


Now with Deferred out of the way, let's go back to your jQuery version and improve it. Since we created Deferred as a constructor, we can subclass from it. This means we can have Foo become a Deferred and have the luxury of the Deferred API.

// Setup Foo as a Deferred in as little as 2 lines.
function Foo(){ Deferred.apply(this, arguments); }
Foo.prototype = Object.create(Deferred.prototype);

// Your Foo API
Foo.prototype.init = function(){
  var instance = this;
  setTimeout(function(){
    instance.resolve();
  }, 2000);
};


In the end, foo will have Deferred powers, care of an extended Promise API.

var foo = new Foo();

// We can now use `then`.
foo.then(function(){
  alert('foo.ready!');
});

//
foo.init();


Now if I wrote the code properly, this should work:



function Deferred(callback) {
  var instance = this;
  this._resolver = null;
  this._rejector = null;
  this._promise = new Promise(function(resolve, reject) {
    instance._resolver = resolve;
    instance._rejector = reject;
  });
  if (typeof callback === 'function')
    callback.call(this, this._resolver, this._rejector);
}

Deferred.prototype.then = function(resolve, reject) { return this._promise.then(resolve, reject); };
Deferred.prototype.resolve = function(resolution) { this._resolver.call(null, resolution); return this; };
Deferred.prototype.reject = function(rejection) { this._rejector.call(null, rejection); return this; };

// The setup code:

function Foo() { Deferred.apply(this, arguments); }
Foo.prototype = Object.create(Deferred.prototype);

Foo.prototype.init = function() {
  var instance = this;
  setTimeout(function() {
    instance.resolve('YAY!');
  }, 2000);
};

// Phew! Now your Foo is a "native" Deferred.

var foo = new Foo();

foo.then(function(value) {
  alert('foo.ready! ' + value);
});

foo.init();