Skip to content

Instantly share code, notes, and snippets.

@EGreg
Last active August 29, 2015 14:00
Show Gist options
  • Save EGreg/11236162 to your computer and use it in GitHub Desktop.
Save EGreg/11236162 to your computer and use it in GitHub Desktop.
Q.Promise
/**
* Q.Promise constructor.
* Call the .fulfill(...) or .reject(...) method to
* signal that the promise is fulfilled or rejected.
* Implemented according to http://promises-aplus.github.io/promises-spec/
* with two exceptions:
* 2.2.5) fulfill and reject can in fact accept "this", and pass it on
* 2.3.3.3.1) the first callback to .then() doesn't treat its arguments specially
*/
Q.Promise = function () {
this.state = Q.Promise.states.PENDING;
this._doneHandlers = [];
this._failHandlers = [];
this._progressHandlers = [];
this._args = null;
this._context = null;
var p = this;
p.fulfill = function () {
if (p.state !== Q.Promise.states.PENDING) {
return p;
}
p.state = Q.Promise.states.FULFILLED;
p._args = Array.prototype.slice.call(arguments, 0);
p._context = this;
setTimeout(function () {
for (var i=0, l=p._doneHandlers.length; i<l; ++i) {
p._doneHandlers[i].apply(p._context, p._args);
}
p._doneHandlers = null;
});
return p;
};
p.reject = function () {
if (p.state !== Q.Promise.states.PENDING) {
return p;
}
p.state = Q.Promise.states.REJECTED;
p._args = Array.prototype.slice.call(arguments, 0);
p._context = this;
setTimeout(function () {
for (var i=0, l=p._failHandlers.length; i<l; ++i) {
p._failHandlers[i].apply(p._context, p._args);
}
p._failHandlers = null;
});
return p;
};
p.notify = function () {
if (p.state !== Q.Promise.states.PENDING) {
return p;
}
var context = this;
var args = Array.prototype.slice.call(arguments, 0);
setTimeout(function () {
for (var i=0, l=p._progressHandlers.length; i<l; ++i) {
p._progressHandlers[i].apply(context, args);
}
p._progressHandlers = null;
});
return p;
};
};
Q.Promise.wait = function () {
var args = Array.prototype.slice.call(arguments, 0);
};
Q.Promise.prototype.then = function (doneHandler, failHandler, progressHandler) {
var result = new Q.Promise();
var t;
try {
switch (this.state) {
case Q.Promise.states.FULFILLED:
if (typeof doneHandler === 'function') { // 2.2.1.1
x = doneHandler.apply(this._context, this._args); // 2.2.2.1
}
break;
case Q.Promise.states.REJECTED:
if (typeof failHandler === 'function') { // 2.2.1.2
x = failHandler.apply(this._context, this._args); // 2.2.3.1
}
break;
default:
if (typeof doneHandler === 'function') { // 2.2.1.1
this._doneHandlers.push(doneHandler); // 2.2.2.2
}
if (typeof failHandler === 'function') { // 2.2.1.2
this._failHandlers.push(failHandler); // 2.2.3.2
}
if (typeof progressHandler === 'function') {
this._progressHandlers.push(progressHandler);
}
break;
}
if (typeof doneHandler !== 'function') {
this.done(result.fulfill); // 2.2.7.3
}
if (typeof failHandler !== 'function') {
this.fail(result.reject); // 2.2.7.4
}
if (x) {
_Q_Promise_resolve(promise, x);
}
} catch (e) {
result.reject(e); // 2.2.7.2
}
return result;
};
function _Q_Promise_resolve(promise, x) {
var t;
if (x instanceof Q.Promise) {
x.done(result.fulfill); // 2.3.2.2
x.fail(result.reject); // 2.3.2.3
} else {
t = x.then; // 2.3.3.1
if (typeof t === 'function') { // 2.3.3.3
// NOTE: the following does not support the spec completely
// in that fulfilling and rejecting promises
// does not handle a special case where a Q.Promise
// or other thenable is passed to the first argument of t
t.apply(x, result.fulfill, result.reject);
} else {
result.fulfill(x); // 2.3.3.4
}
}
}
Q.Promise.prototype.done = function (doneHandler) {
return this.then(doneHandler);
};
Q.Promise.prototype.fail = function (failHandler) {
return this.then(null, failHandler);
};
Q.Promise.prototype.progress = function (progressHandler) {
return this.then(null, null, progressHandler);
};
Q.Promise.prototype.always = function (handler) {
return this.then(handler, handler);
};
Q.Promise.prototype.isPending = function () {
return this.state === Q.Promise.states.PENDING;
};
Q.Promise.prototype.isFulfilled = function () {
return this.state === Q.Promise.states.FULFILLED;
};
Q.Promise.prototype.isRejected = function () {
return this.state === Q.Promise.states.REJECTED;
};
Q.Promise.states = {
PENDING: 'pending',
FULFILLED: 'fulfilled',
REJECTED: 'rejected'
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment