Last active
December 10, 2015 12:38
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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