Skip to content
This repository was archived by the owner on Nov 9, 2017. It is now read-only.

Commit 08fecbf

Browse files
committed
Basic promise implementation that completes a lot of the spec
1 parent 5eb888d commit 08fecbf

File tree

2 files changed

+144
-3
lines changed

2 files changed

+144
-3
lines changed

lib/promise.js

Lines changed: 128 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,138 @@
1+
var EventEmitter = require('events').EventEmitter;
2+
13
/**
24
* Promises states according to the A+ Specification
5+
*
36
* @readOnly
4-
* @enum {number}
7+
* @enum {Number}
58
*/
69
var State = {
710
PENDING: 0,
811
FULFILLED: 1,
912
REJECTED: 2
1013
};
1114

12-
var Promise = module.exports = function() {};
15+
/**
16+
* Executes a function asynchronously. Prefers setImmediate but will fallback to
17+
* process.nextTick for older versions of node.
18+
*
19+
* @type {Function}
20+
*/
21+
var next = typeof setImmediate === 'function' ? setImmediate : procees.nextTick;
22+
23+
/**
24+
* Tests if a variable is a function
25+
*
26+
* @param {Object} arg The argument to test
27+
* @return {Boolean} Whether or not the argument is a function
28+
*/
29+
var isFunction = function(arg) {
30+
return typeof arg === 'function';
31+
};
32+
33+
/**
34+
* Triggers the promise events
35+
*
36+
* @param {Promise} promise The promise to trigger
37+
*/
38+
var trigger = function(promise) {
39+
if (promise.state === State.FULFILLED) {
40+
next(function() {
41+
promise.events.emit('fulfilled', promise.value)
42+
});
43+
}
44+
45+
if (promise.state === State.REJECTED) {
46+
next(function() {
47+
promise.events.emit('rejected', promise.value);
48+
});
49+
}
50+
};
51+
52+
/**
53+
* Resolves a promise and triggers the promises
54+
*
55+
* @param {Promise} promise The promise to resolve
56+
* @param {State} state The new state of the promise
57+
* @param {Object} value The new value of the promise
58+
* @return {State} The current state of the promise
59+
*/
60+
var resolve = function(promise, state, value) {
61+
// Cannot transition from FULFILLED or REJECTED
62+
if (promise.state === State.FULFILLED || promise.state === State.REJECTED) {
63+
return promise.state;
64+
}
65+
66+
// Must have a value for value
67+
if (state === State.FULFILLED && arguments.length < 3) {
68+
return promise.state;
69+
}
70+
71+
// Must have a reason for rejected
72+
if (state === State.REJECTED && !value) {
73+
return promise.state;
74+
}
75+
76+
promise.state = state;
77+
promise.value = value;
78+
79+
trigger(promise);
80+
81+
return promise.state;
82+
};
83+
84+
/**
85+
* Promise
86+
* @class Promise implementing the Promises/A+ Specification
87+
*/
88+
var Promise = module.exports = function() {
89+
this.state = State.PENDING;
90+
this.events = new EventEmitter();
91+
};
92+
93+
/**
94+
* A+ Then specification
95+
*
96+
* @param {Function} onFulfilled Called when the promise succeeds
97+
* @param {Function} onRejected Called when the promise fails
98+
* @return {Promise} The new promise
99+
*/
100+
Promise.prototype.then = function(onFulfilled, onRejected) {
101+
var promise = new Promise();
102+
103+
if (isFunction(onFulfilled)) {
104+
this.events.once('fulfilled', function(value) {
105+
promise.fulfill(onFulfilled(value));
106+
});
107+
}
108+
109+
if (isFunction(onRejected)) {
110+
this.events.once('rejected', function(reason) {
111+
promise.reject(onRejected(reason));
112+
});
113+
}
114+
115+
trigger(this);
116+
117+
return promise;
118+
};
119+
120+
/**
121+
* Fulfills the promise with a value
122+
*
123+
* @param {Object} value
124+
* @return {State} The state of the promise
125+
*/
126+
Promise.prototype.fulfill = function(value) {
127+
return resolve(this, State.FULFILLED, value);
128+
};
129+
130+
/**
131+
* Rejects the promise with a reason
132+
*
133+
* @param {Error} reason The reason the promise failed
134+
* @return {State} The state of the promise
135+
*/
136+
Promise.prototype.reject = function(reason) {
137+
resolve(this, State.REJECTED, reason);
138+
};

test/promises_spec.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,21 @@
11
var Promise = require('../lib/promise');
22
var aplus = require('promises-aplus-tests');
33

4+
var adapter = {
5+
pending: function() {
6+
var promise = new Promise();
7+
return {
8+
promise: promise,
9+
fulfill: function(value) {
10+
promise.fulfill(value);
11+
},
12+
reject: function(reason) {
13+
promise.reject(reason);
14+
}
15+
};
16+
}
17+
};
18+
419
describe('Promises/A+ tests', function() {
5-
aplus.mocha(Promise);
20+
aplus.mocha(adapter);
621
});

0 commit comments

Comments
 (0)