concurrent
Version:
Promises/A+ with Scala awesomeness
132 lines (110 loc) • 3.15 kB
JavaScript
var next = require('./next');
var State = require('./state');
/**
* Resolves the promises asynchronously
*
* @param {Promise} promise The promise to resolve
*/
var resolve = function(promise) {
var resolver;
resolver = function() {
var callback = promise.callbacks.shift();
if (callback && callback[promise.state]) {
callback[promise.state](promise.value);
}
if (promise.callbacks.length > 0) {
next(resolver);
}
};
next(resolver);
};
/**
* Transitions a promise from one state to another
*
* @param {Promise} promise The promise to transition
* @param {State} state The new state of the promise
* @param {Object} value The value or reason of the promise
* @return {State} The state of the promise
*/
var transition = function(promise, state, value) {
if (promise.state === State.FULFILLED || promise.state === State.REJECTED) {
return promise.state;
}
promise.state = state;
promise.value = value;
resolve(promise);
return promise.state;
};
/**
* Handles the promise chain and returns a new promise
*
* @param {Promise} promise The promise to initialize
* @param {State} state The state for the promise to transition to
* @param {Function} fn The callback from then
* @return {Function} The callback to be registered
*/
var handle = function(promise, state, fn) {
return function(value) {
if (typeof fn !== 'function') {
return transition(promise, state, value);
}
try {
var result = fn(value);
if (result && typeof result.then === 'function') {
result.then(function(value) {
promise.fulfill(value);
}, function(reason) {
promise.reject(reason);
});
return promise.state;
}
return promise.fulfill(result);
} catch (reason) {
return promise.reject(reason);
}
};
};
/**
* Promise implementing the Promises/A+ Specification
* @class
*/
var Promise = module.exports = function() {
this.state = State.PENDING;
this.callbacks = [];
};
/**
* A+ Then specification
*
* @param {Function} onFulfilled Called when the promise succeeds
* @param {Function} onRejected Called when the promise fails
* @return {Promise} The new promise
*/
Promise.prototype.then = function(onFulfilled, onRejected) {
var promise = new this.constructor();
var callbacks = {};
callbacks[State.FULFILLED] = handle(promise, State.FULFILLED, onFulfilled);
callbacks[State.REJECTED] = handle(promise, State.REJECTED, onRejected);
this.callbacks.push(callbacks);
if (this.state !== State.PENDING) {
resolve(this);
}
return promise;
};
/**
* Fulfills the promise with a value
*
* @param {Object} value
* @return {State} The state of the promise
*/
Promise.prototype.fulfill = function(value) {
transition(this, State.FULFILLED, value);
};
/**
* Rejects the promise with a reason
*
* @param {Error} reason The reason the promise failed
* @return {State} The state of the promise
*/
Promise.prototype.reject = function(reason) {
transition(this, State.REJECTED, reason);
};