Skip to content

Instantly share code, notes, and snippets.

@m4rw3r
Last active December 10, 2015 12:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save m4rw3r/4435404 to your computer and use it in GitHub Desktop.
Save m4rw3r/4435404 to your computer and use it in GitHub Desktop.
Minimalistic CommonJS Promises/A implementation fulfilling all the tests in https://github.com/domenic/promise-tests and also all tests in https://github.com/promises-aplus/promises-tests
(function() {
/**
* Defers the execution of func to the next tick.
*
* @param function
*/
var deferFunc = this.nextTick
? this.nextTick
: function(func) {
setTimeout(func, 0);
};
/**
* Performs the resolution logic.
*
* @oaram string method name to call on the next promise in case the
* callback is not a function
* @param Promise next promise in the chain
* @param function resolve/reject callback to call
* @param value value to pass to resolve/reject callback
* @return void
*/
var doResolve = function(default_action, next, callback, value)
{
if(typeof callback != 'function') {
return next[default_action](value);
}
try {
var ret = callback(value);
if(ret !== null && typeof ret == 'object' && typeof ret['then'] == 'function') {
ret.then(function(value) {
next.resolve(value);
}, function(reason) {
next.reject(reason);
});
}
else {
next.resolve(ret);
}
}
catch(e) {
next.reject(e);
}
};
var Deferred = function()
{
this.next = [];
};
Deferred.prototype.then = function(onResolved, onRejected)
{
var next = new Deferred();
this.next.push([next, onResolved, onRejected]);
return next;
};
Deferred.prototype.resolve = function(value)
{
if(this.then != Deferred.prototype.then) {
return;
}
this.then = Resolved.prototype.then;
this.value = value;
for(var l = this.next.length, i = 0; i < l; i++) {
doResolve("resolve", this.next[i][0], this.next[i][1], value)
}
};
Deferred.prototype.reject = function(reason)
{
if(this.then != Deferred.prototype.then) {
return;
}
this.then = Rejected.prototype.then;
this.reason = reason;
for(var l = this.next.length, i = 0; i < l; i++) {
doResolve("reject", this.next[i][0], this.next[i][2], reason)
}
};
var Resolved = function(value)
{
this.value = value;
};
Resolved.prototype = new Deferred();
Resolved.prototype.then = function(onResolved)
{
var next = new Deferred(),
value = this.value;
deferFunc(function() {
doResolve("resolve", next, onResolved, value);
});
return next;
};
var Rejected = function(reason)
{
this.reason = reason;
};
Rejected.prototype = new Deferred();
Rejected.prototype.then = function(_, onRejected)
{
var next = new Deferred(),
reason = this.reason;
deferFunc(function() {
doResolve("reject", next, onRejected, reason);
});
return next;
};
/**
* Returns a new deferred promise which will be resolved once all the parameters
* have been resolved, if any of them gets rejected the returned promise will
* also become rejected.
*
* @param Promise
* @param ...
* @return Promise
*/
var and = function(/* ... */) {
var d = new Deferred(),
length = arguments.length,
resolved = 0;
for(var i = 0; i < length; i++) {
arguments[i].then(function(value) {
if(++resolved == length) {
/* TODO: What to send here */
d.resolve(value);
}
}, function(reason) {
/* Fail on the first reject */
d.reject(reason);
});
}
return d;
};
/**
* Returns a new deferred promise which will be resolved if any of the parameters
* becomes resolved, if all of them are rejected the returned promise will also
* also become rejected.
*
* @param Promise
* @param ...
* @return Promise
*/
var or = function(/* ... */) {
var d = new Deferred(),
length = arguments.length,
rejected = 0;
for(var i = 0; i < length; i++) {
arguments[i].then(function(value) {
/* Succeed on the first resolution */
d.resolve(value);
}, function(reason) {
if(++rejected == length) {
/* TODO: What reason do we give here? */
d.reject(reason);
}
});
}
return d;
};
Deferred.Resolved = Resolved;
Deferred.Rejected = Rejected;
Deferred.and = and;
Deferred.or = or;
if(typeof exports !== 'undefined') {
exports.Promise = Deferred;
}
else {
this.Promise = Deferred;
}
}).call(this);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment