Skip to content

Instantly share code, notes, and snippets.

@goliatone
Forked from briancavalier/semantic tiny Promise.js
Last active December 27, 2015 08:39
Show Gist options
  • Save goliatone/7298127 to your computer and use it in GitHub Desktop.
Save goliatone/7298127 to your computer and use it in GitHub Desktop.
function Promise () {
this._resolve = [];
this._reject = [];
this._progress = [];
}
Promise.prototype = {
/* This is the "front end" API. */
// Shooting for a more semantic Promise client API.
// Allows chains like:
// promise.then(success).or(fail).using(progress)
then: function (onResolve) {
// capture calls to then()
this._resolve.push(onResolve);
return this;
},
or: function(onReject) {
this._reject.push(onReject);
return this;
},
using: function(onProgress) {
this._progress.push(onProgress);
return this;
},
// Some promise implementations also have a cancel() front end API that
// calls all of the onReject() callbacks (aka a "cancelable promise").
// cancel: function (reason) {},
/* This is the "back end" API. */
// resolve(resolvedValue): The resolve() method is called when a promise
// is resolved (duh). The resolved value (if any) is passed by the resolver
// to this method. All waiting onResolve callbacks are called
// and any future ones are, too, each being passed the resolved value.
resolve: function (val) { this._complete(this._resolve, val, 'then', 'or'); },
// reject(exception): The reject() method is called when a promise cannot
// be resolved. Typically, you'd pass an exception as the single parameter,
// but any other argument, including none at all, is acceptable.
// All waiting and all future onReject callbacks are called when reject()
// is called and are passed the exception parameter.
reject: function (ex) { this._complete(this._reject, ex, 'or', 'then'); },
// Some promises may have a progress handler. The back end API to signal a
// progress "event" has a single parameter. The contents of this parameter
// could be just about anything and is specific to your implementation.
progress: function(statusObject) {
var i=0,
progressFunc;
while(progressFunc = this._progress[i++]) { progressFunc(statusObject); }
},
/* "Private" methods. */
// Passing in the actual array, and which func to make immediate/disable,
// thanks to @unscriptable for the ideas
_complete: function (cbs, arg, immediate, disable) {
// Always disble using(), and also the func that was passed in to be disabled
var noop = this.using = this[disable] =
function() { return this; };
// switch over to sync then() or or()
this[immediate] = function(cb) {cb && cb(arg); return this; }
// disallow multiple calls to resolve or reject
this.resolve = this.reject = this.progress =
function () { throw new Error('Promise already completed.'); };
// complete all waiting (async) then()s
var cb,
i = 0;
while (cb = cbs[i++]) { cb(arg); }
delete this._resolve;
delete this._reject;
delete this._progress;
}
};
var Promise = function(wrappedFn, wrappedThis) {
this.then = function(wrappedFn, wrappedThis) {
this.next = new Promise(wrappedFn, wrappedThis);
return this.next;
};
this.run = function() {
wrappedFn.promise = this;
wrappedFn.apply(wrappedThis);
};
this.complete = function() {
if (this.next) {
this.next.run();
}
};
};
Promise.create = function(func) {
if (func.hasOwnProperty('promise')) {
return func.promise;
} else {
return new Promise();
}
};
// --------------------------------------------- Promise Implementation
Promise = function () {
this._stack = [];
this._isResolved = false;
}
Promise.prototype = {
success: function(callback){
// Is the promise already resolved?
if(this._isResolved) {
callback( this._result );
} else {
this._stack.push(callback);
}
},
resolve: function(result){
if(this._isResolved) { throw new Error('Promise Has Already Resolved'); }
this._result = result;
// Clear the promise stack
for (var i = 0; i < this._stack.length; i++) {
this._stack[i](result);
}
this._isResolved = true
}
}
// --------------------------------------------- "Assertions"
var foo = new Promise();
var bar = new Promise();
foo.resolve('hello');
setTimeout(function(){
bar.resolve('world');
}, 500);
foo.success(function(result){
console.log(result);
});
bar.success(function(result){
console.log(result);
});
// => "hello"
// ... 500ms later ...
// => "world"
//A promise should only be able to be resolved once. Subsequent calls to resolve should error.
setTimeout(function(){
bar.resolve('world again'); // Throws error
}, 750);
//A promise may have multiple success callbacks attatched.
setTimeout(function(){
var baz = new Promise();
baz.success(function(result){ console.log(result); });
baz.success(function(result){ console.log(result.split('').reverse().join('')); });
baz.resolve('Rainbow Bunny!');
}, 1000);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment