Skip to content

Commit 33875c1

Browse files
author
John Haley
committed
Added synchronous methods and tests
1 parent cac6f9d commit 33875c1

File tree

2 files changed

+290
-0
lines changed

2 files changed

+290
-0
lines changed

lib/core.js

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
'use strict';
2+
3+
var asap = require('asap')
4+
5+
module.exports = Promise;
6+
function Promise(fn) {
7+
if (typeof this !== 'object') throw new TypeError('Promises must be constructed via new')
8+
if (typeof fn !== 'function') throw new TypeError('not a function')
9+
var state = null
10+
var value = null
11+
var deferreds = []
12+
var self = this
13+
14+
this.then = function(onFulfilled, onRejected) {
15+
return new self.constructor(function(resolve, reject) {
16+
handle(new Handler(onFulfilled, onRejected, resolve, reject))
17+
})
18+
}
19+
20+
this.state = state;
21+
22+
this.isFulfilled = function () {
23+
debugger;
24+
return state === true
25+
}
26+
27+
this.isRejected = function () {
28+
return state === false
29+
}
30+
31+
this.isPending = function () {
32+
return state === null
33+
}
34+
35+
this.value = function () {
36+
if (!this.isFulfilled()) {
37+
throw new Error('Cannot get a value of an unfulfilled promise.')
38+
}
39+
40+
return value
41+
}
42+
43+
this.reason = function () {
44+
if (!this.isRejected()) {
45+
throw new Error('Cannot get a rejection reason of a non-rejected promise.')
46+
}
47+
48+
return reason
49+
}
50+
51+
function handle(deferred) {
52+
if (state === null) {
53+
deferreds.push(deferred)
54+
return
55+
}
56+
asap(function() {
57+
var cb = state ? deferred.onFulfilled : deferred.onRejected
58+
if (cb === null) {
59+
(state ? deferred.resolve : deferred.reject)(value)
60+
return
61+
}
62+
var ret
63+
try {
64+
ret = cb(value)
65+
}
66+
catch (e) {
67+
deferred.reject(e)
68+
return
69+
}
70+
deferred.resolve(ret)
71+
})
72+
}
73+
74+
function resolve(newValue) {
75+
try { //Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
76+
if (newValue === self) throw new TypeError('A promise cannot be resolved with itself.')
77+
if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) {
78+
var then = newValue.then
79+
if (typeof then === 'function') {
80+
doResolve(then.bind(newValue), resolve, reject)
81+
return
82+
}
83+
}
84+
state = true
85+
value = newValue
86+
finale()
87+
} catch (e) { reject(e) }
88+
}
89+
90+
function reject(newValue) {
91+
state = false
92+
value = newValue
93+
finale()
94+
}
95+
96+
function finale() {
97+
for (var i = 0, len = deferreds.length; i < len; i++)
98+
handle(deferreds[i])
99+
deferreds = null
100+
}
101+
102+
doResolve(fn, resolve, reject)
103+
}
104+
105+
106+
function Handler(onFulfilled, onRejected, resolve, reject){
107+
this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null
108+
this.onRejected = typeof onRejected === 'function' ? onRejected : null
109+
this.resolve = resolve
110+
this.reject = reject
111+
}
112+
113+
/**
114+
* Take a potentially misbehaving resolver function and make sure
115+
* onFulfilled and onRejected are only called once.
116+
*
117+
* Makes no guarantees about asynchrony.
118+
*/
119+
function doResolve(fn, onFulfilled, onRejected) {
120+
var done = false;
121+
try {
122+
fn(function (value) {
123+
if (done) return
124+
done = true
125+
onFulfilled(value)
126+
}, function (reason) {
127+
if (done) return
128+
done = true
129+
onRejected(reason)
130+
})
131+
} catch (ex) {
132+
if (done) return
133+
done = true
134+
onRejected(ex)
135+
}
136+
}
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
var assert = require('better-assert');
2+
var Promise = require('../');
3+
4+
describe('synchronous-inspection-tests', function () {
5+
it('can poll a promise to see if it is resolved', function () {
6+
var finished = null;
7+
var fulfilledPromise = new Promise(function(resolve, reject) {
8+
var interval = setInterval(function() {
9+
if (finished !== null) {
10+
clearTimeout(interval);
11+
12+
if (finished) {
13+
resolve(true);
14+
}
15+
else {
16+
reject(false);
17+
}
18+
}
19+
}, 10);
20+
});
21+
22+
assert(fulfilledPromise.isPending());
23+
assert(!fulfilledPromise.isFulfilled());
24+
assert(!fulfilledPromise.isRejected());
25+
26+
finished = true;
27+
28+
setTimeout(function () {
29+
assert(fulfilledPromise.isFulfilled());
30+
assert(!fulfilledPromise.isRejected());
31+
assert(fulfilledPromise.value());
32+
assert(!fulfilledPromise.isPending());
33+
}, 30);
34+
});
35+
36+
it('can poll a promise to see if it is rejected', function () {
37+
var finished = null;
38+
var fulfilledPromise = new Promise(function(resolve, reject) {
39+
var interval = setInterval(function() {
40+
if (finished !== null) {
41+
clearTimeout(interval);
42+
43+
if (finished) {
44+
resolve(true);
45+
}
46+
else {
47+
reject(false);
48+
}
49+
}
50+
}, 10);
51+
});
52+
53+
assert(fulfilledPromise.isPending());
54+
assert(!fulfilledPromise.isFulfilled());
55+
assert(!fulfilledPromise.isRejected());
56+
57+
finished = false;
58+
59+
setTimeout(function () {
60+
assert(!fulfilledPromise.isFulfilled());
61+
assert(fulfilledPromise.isRejected());
62+
assert(!fulfilledPromise.reason());
63+
assert(!fulfilledPromise.isPending());
64+
}, 30);
65+
});
66+
67+
it('will throw an error if getting a value of an unfulfilled promise', function () {
68+
var finished = null;
69+
var fulfilledPromise = new Promise(function(resolve, reject) {
70+
var interval = setInterval(function() {
71+
if (finished !== null) {
72+
clearTimeout(interval);
73+
74+
if (finished) {
75+
resolve(true);
76+
}
77+
else {
78+
reject(false);
79+
}
80+
}
81+
}, 10);
82+
});
83+
84+
assert(fulfilledPromise.isPending());
85+
assert(!fulfilledPromise.isFulfilled());
86+
assert(!fulfilledPromise.isRejected());
87+
88+
try {
89+
fulfilledPromise.value();
90+
91+
assert(false);
92+
}
93+
catch (e) {
94+
assert(true);
95+
}
96+
97+
finished = false;
98+
99+
setTimeout(function () {
100+
try {
101+
fulfilledPromise.value();
102+
103+
assert(false);
104+
}
105+
catch (e) {
106+
assert(true);
107+
}
108+
}, 30);
109+
});
110+
111+
it('will throw an error if getting a reason of a non-rejected promise', function () {
112+
var finished = null;
113+
var fulfilledPromise = new Promise(function(resolve, reject) {
114+
var interval = setInterval(function() {
115+
if (finished !== null) {
116+
clearTimeout(interval);
117+
118+
if (finished) {
119+
resolve(true);
120+
}
121+
else {
122+
reject(false);
123+
}
124+
}
125+
}, 10);
126+
});
127+
128+
assert(fulfilledPromise.isPending());
129+
assert(!fulfilledPromise.isFulfilled());
130+
assert(!fulfilledPromise.isRejected());
131+
132+
try {
133+
fulfilledPromise.reason();
134+
135+
assert(false);
136+
}
137+
catch (e) {
138+
assert(true);
139+
}
140+
141+
finished = true;
142+
143+
setTimeout(function () {
144+
try {
145+
fulfilledPromise.reason();
146+
147+
assert(false);
148+
}
149+
catch (e) {
150+
assert(true);
151+
}
152+
}, 30);
153+
});
154+
});

0 commit comments

Comments
 (0)