Skip to content

Commit 7fb4bfb

Browse files
author
John Haley
committed
Version bump to 2.0.1
1 parent faeed88 commit 7fb4bfb

File tree

2 files changed

+160
-97
lines changed

2 files changed

+160
-97
lines changed

lib/core.js

+159-96
Original file line numberDiff line numberDiff line change
@@ -1,112 +1,177 @@
11
'use strict';
22

3-
var asap = require('asap')
3+
var asap = require('asap/raw')
4+
5+
function noop() {};
6+
7+
// States:
8+
//
9+
// 0 - pending
10+
// 1 - fulfilled with _value
11+
// 2 - rejected with _value
12+
// 3 - adopted the state of another promise, _value
13+
//
14+
// once the state is no longer pending (0) it is immutable
15+
16+
// All `_` prefixed properties will be reduced to `_{random number}`
17+
// at build time to obfuscate them and discourage their use.
18+
// We don't use symbols or Object.defineProperty to fully hide them
19+
// because the performance isn't good enough.
20+
21+
22+
// to avoid using try/catch inside critical functions, we
23+
// extract them to here.
24+
var LAST_ERROR = null;
25+
var IS_ERROR = {};
26+
function getThen(obj) {
27+
try {
28+
return obj.then;
29+
} catch (ex) {
30+
LAST_ERROR = ex;
31+
return IS_ERROR;
32+
}
33+
}
34+
35+
function tryCallOne(fn, a) {
36+
try {
37+
return fn(a);
38+
} catch (ex) {
39+
LAST_ERROR = ex;
40+
return IS_ERROR;
41+
}
42+
}
43+
function tryCallTwo(fn, a, b) {
44+
try {
45+
fn(a, b);
46+
} catch (ex) {
47+
LAST_ERROR = ex;
48+
return IS_ERROR;
49+
}
50+
}
451

552
module.exports = Promise;
653
function Promise(fn) {
754
if (typeof this !== 'object') throw new TypeError('Promises must be constructed via new')
855
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-
return state === true
24-
}
25-
26-
this.isRejected = function () {
27-
return state === false
56+
this._23 = 0;
57+
this._78 = null;
58+
this._29 = [];
59+
if (fn === noop) return;
60+
doResolve(fn, this);
61+
}
62+
Promise.prototype._7 = function (onFulfilled, onRejected) {
63+
var self = this;
64+
return new this.constructor(function (resolve, reject) {
65+
var res = new Promise(noop);
66+
res.then(resolve, reject);
67+
self._3(new Handler(onFulfilled, onRejected, res));
68+
});
69+
};
70+
Promise.prototype.then = function(onFulfilled, onRejected) {
71+
if (this.constructor !== Promise) return this._7(onFulfilled, onRejected);
72+
var res = new Promise(noop);
73+
this._3(new Handler(onFulfilled, onRejected, res));
74+
return res;
75+
};
76+
Promise.prototype._3 = function(deferred) {
77+
if (this._23 === 3) {
78+
this._78._3(deferred);
79+
return;
2880
}
29-
30-
this.isPending = function () {
31-
return state === null
81+
if (this._23 === 0) {
82+
this._29.push(deferred);
83+
return;
3284
}
33-
34-
this.value = function () {
35-
if (!self.isFulfilled()) {
36-
throw new Error('Cannot get a value of an unfulfilled promise.')
85+
var state = this._23;
86+
var value = this._78;
87+
asap(function() {
88+
var cb = state === 1 ? deferred.onFulfilled : deferred.onRejected
89+
if (cb === null) {
90+
(state === 1 ? deferred.promise._67(value) : deferred.promise._30(value))
91+
return
3792
}
38-
39-
return value
40-
}
41-
42-
this.reason = function () {
43-
if (!self.isRejected()) {
44-
throw new Error('Cannot get a rejection reason of a non-rejected promise.')
93+
var ret = tryCallOne(cb, value);
94+
if (ret === IS_ERROR) {
95+
deferred.promise._30(LAST_ERROR)
96+
} else {
97+
deferred.promise._67(ret)
4598
}
46-
47-
return reason
99+
});
100+
};
101+
Promise.prototype._67 = function(newValue) {
102+
//Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
103+
if (newValue === this) {
104+
return this._30(new TypeError('A promise cannot be resolved with itself.'))
48105
}
49-
50-
function handle(deferred) {
51-
if (state === null) {
52-
deferreds.push(deferred)
53-
return
106+
if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) {
107+
var then = getThen(newValue);
108+
if (then === IS_ERROR) {
109+
return this._30(LAST_ERROR);
54110
}
55-
asap(function() {
56-
var cb = state ? deferred.onFulfilled : deferred.onRejected
57-
if (cb === null) {
58-
(state ? deferred.resolve : deferred.reject)(value)
59-
return
60-
}
61-
var ret
62-
try {
63-
ret = cb(value)
64-
}
65-
catch (e) {
66-
deferred.reject(e)
67-
return
111+
if (
112+
then === this.then &&
113+
newValue instanceof Promise &&
114+
newValue._3 === this._3
115+
) {
116+
this._23 = 3;
117+
this._78 = newValue;
118+
for (var i = 0; i < this._29.length; i++) {
119+
newValue._3(this._29[i]);
68120
}
69-
deferred.resolve(ret)
70-
})
121+
return;
122+
} else if (typeof then === 'function') {
123+
doResolve(then.bind(newValue), this)
124+
return
125+
}
71126
}
127+
this._23 = 1
128+
this._78 = newValue
129+
this._27()
130+
}
72131

73-
function resolve(newValue) {
74-
try { //Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
75-
if (newValue === self) throw new TypeError('A promise cannot be resolved with itself.')
76-
if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) {
77-
var then = newValue.then
78-
if (typeof then === 'function') {
79-
doResolve(then.bind(newValue), resolve, reject)
80-
return
81-
}
82-
}
83-
state = true
84-
value = newValue
85-
finale()
86-
} catch (e) { reject(e) }
87-
}
132+
Promise.prototype._30 = function (newValue) {
133+
this._23 = 2
134+
this._78 = newValue
135+
this._27()
136+
}
137+
Promise.prototype._27 = function () {
138+
for (var i = 0; i < this._29.length; i++)
139+
this._3(this._29[i])
140+
this._29 = null
141+
}
88142

89-
function reject(newValue) {
90-
state = false
91-
value = newValue
92-
finale()
143+
/**
144+
* Synchronous Inspection
145+
*/
146+
Promise.prototype.isPending = function () {
147+
return this._23 === 0;
148+
}
149+
Promise.prototype.isFulfilled = function () {
150+
return this._23 === 1 || this._23 === 3;
151+
}
152+
Promise.prototype.isRejected = function () {
153+
return this._23 === 2;
154+
}
155+
Promise.prototype.value = function () {
156+
if (!this.isFulfilled()) {
157+
throw new Error('Cannot get a value of an unfulfilled promise.')
93158
}
94159

95-
function finale() {
96-
for (var i = 0, len = deferreds.length; i < len; i++)
97-
handle(deferreds[i])
98-
deferreds = null
160+
return this._78;
161+
}
162+
Promise.prototype.reason = function () {
163+
if (!this.isRejected()) {
164+
throw new Error('Cannot get a rejection reason of a non-rejected promise.')
99165
}
100166

101-
doResolve(fn, resolve, reject)
167+
return this._78;
102168
}
103169

104170

105-
function Handler(onFulfilled, onRejected, resolve, reject){
171+
function Handler(onFulfilled, onRejected, promise){
106172
this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null
107173
this.onRejected = typeof onRejected === 'function' ? onRejected : null
108-
this.resolve = resolve
109-
this.reject = reject
174+
this.promise = promise;
110175
}
111176

112177
/**
@@ -115,21 +180,19 @@ function Handler(onFulfilled, onRejected, resolve, reject){
115180
*
116181
* Makes no guarantees about asynchrony.
117182
*/
118-
function doResolve(fn, onFulfilled, onRejected) {
183+
function doResolve(fn, promise) {
119184
var done = false;
120-
try {
121-
fn(function (value) {
122-
if (done) return
123-
done = true
124-
onFulfilled(value)
125-
}, function (reason) {
126-
if (done) return
127-
done = true
128-
onRejected(reason)
129-
})
130-
} catch (ex) {
185+
var res = tryCallTwo(fn, function (value) {
131186
if (done) return
132187
done = true
133-
onRejected(ex)
188+
promise._67(value)
189+
}, function (reason) {
190+
if (done) return
191+
done = true
192+
promise._30(reason)
193+
})
194+
if (!done && res === IS_ERROR) {
195+
done = true
196+
promise._30(LAST_ERROR)
134197
}
135198
}

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "nodegit-promise",
3-
"version": "2.0.0",
3+
"version": "2.0.1",
44
"description": "Bare bones Promises/A+ implementation with synchronous inspection",
55
"main": "index.js",
66
"scripts": {

0 commit comments

Comments
 (0)