faye
Version:
Simple pub/sub messaging for the web
173 lines (132 loc) • 3.72 kB
JavaScript
;
var asap = require('asap');
var PENDING = -1,
FULFILLED = 0,
REJECTED = 1;
var Promise = function(task) {
this._state = PENDING;
this._value = null;
this._defer = [];
execute(this, task);
};
Promise.prototype.then = function(onFulfilled, onRejected) {
var promise = new Promise();
var deferred = {
promise: promise,
onFulfilled: onFulfilled,
onRejected: onRejected
};
if (this._state === PENDING)
this._defer.push(deferred);
else
propagate(this, deferred);
return promise;
};
Promise.prototype['catch'] = function(onRejected) {
return this.then(null, onRejected);
};
var execute = function(promise, task) {
if (typeof task !== 'function') return;
var calls = 0;
var resolvePromise = function(value) {
if (calls++ === 0) resolve(promise, value);
};
var rejectPromise = function(reason) {
if (calls++ === 0) reject(promise, reason);
};
try {
task(resolvePromise, rejectPromise);
} catch (error) {
rejectPromise(error);
}
};
var propagate = function(promise, deferred) {
var state = promise._state,
value = promise._value,
next = deferred.promise,
handler = [deferred.onFulfilled, deferred.onRejected][state],
pass = [resolve, reject][state];
if (typeof handler !== 'function')
return pass(next, value);
asap(function() {
try {
resolve(next, handler(value));
} catch (error) {
reject(next, error);
}
});
};
var resolve = function(promise, value) {
if (promise === value)
return reject(promise, new TypeError('Recursive promise chain detected'));
var then;
try {
then = getThen(value);
} catch (error) {
return reject(promise, error);
}
if (!then) return fulfill(promise, value);
execute(promise, function(resolvePromise, rejectPromise) {
then.call(value, resolvePromise, rejectPromise);
});
};
var getThen = function(value) {
var type = typeof value,
then = (type === 'object' || type === 'function') && value && value.then;
return (typeof then === 'function')
? then
: null;
};
var fulfill = function(promise, value) {
settle(promise, FULFILLED, value);
};
var reject = function(promise, reason) {
settle(promise, REJECTED, reason);
};
var settle = function(promise, state, value) {
var defer = promise._defer, i = 0;
promise._state = state;
promise._value = value;
promise._defer = null;
if (defer.length === 0) return;
while (i < defer.length) propagate(promise, defer[i++]);
};
Promise.resolve = function(value) {
try {
if (getThen(value)) return value;
} catch (error) {
return Promise.reject(error);
}
return new Promise(function(resolve, reject) { resolve(value) });
};
Promise.reject = function(reason) {
return new Promise(function(resolve, reject) { reject(reason) });
};
Promise.all = function(promises) {
return new Promise(function(resolve, reject) {
var list = [], n = promises.length, i;
if (n === 0) return resolve(list);
var push = function(promise, i) {
Promise.resolve(promise).then(function(value) {
list[i] = value;
if (--n === 0) resolve(list);
}, reject);
};
for (i = 0; i < n; i++) push(promises[i], i);
});
};
Promise.race = function(promises) {
return new Promise(function(resolve, reject) {
for (var i = 0, n = promises.length; i < n; i++)
Promise.resolve(promises[i]).then(resolve, reject);
});
};
Promise.deferred = function() {
var tuple = {};
tuple.promise = new Promise(function(resolve, reject) {
tuple.resolve = resolve;
tuple.reject = reject;
});
return tuple;
};
module.exports = Promise;