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