Skip to content

Commit 08078ca

Browse files
author
alanclarke
committed
add /extra.js + refactor tests + improve async rejection chains
1 parent f4121c4 commit 08078ca

15 files changed

+473
-514
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ A tiny sync promise lib
55
sync-p is 52 lines of code, but handles the majority of the promise spec. Some of the spec is intentionally avoided, e.g.
66
- sync-p is capable of returning synchronously, i.e. it does not defer everything
77
- to avoid bloat sync-p assumes that truthy args passed to .then are functions (strict adherence involves explicitly handling cases like .then(5) ...like, just don't do that, that's not the api)
8-
- it doesn't handle the case where promise x is given a handler that returns promise x, causing an infinite promise recursion, because why would you ever do that
8+
- it does not throw if a promise is rejected and no reject handler is attached
99

1010
## installation
1111
```

deferred.js defer.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
var promise = require('./index')
1+
var Promise = require('./index')
22
module.exports = function deferred () {
33
var _resolve, _reject
4-
var _promise = promise(function (resolve, reject) {
4+
var _promise = Promise(function (resolve, reject) {
55
_resolve = resolve
66
_reject = reject
77
})

extra.js

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
var Promise = require('./index')
2+
Promise.all = require('./all')
3+
Promise.resolve = require('./resolve')
4+
Promise.reject = require('./reject')
5+
Promise.defer = require('./defer')
6+
module.exports = Promise

index.js

+10-11
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
module.exports = function promise (resolver) {
22
var waiting = { res: [], rej: [] }
3-
var p = { 'then': then, 'catch': thenCatch }
43
var reject = api(false)
54
var resolve = api(true, reject)
5+
var p = { 'then': then, 'catch': function thenCatch (onReject) {
6+
return then(null, onReject)
7+
}}
68
try { resolver(resolve, reject) } catch (e) {
79
p.status = false
810
p.value = e
@@ -12,33 +14,30 @@ module.exports = function promise (resolver) {
1214
function api (status, reject) {
1315
return function end (val) {
1416
if (typeof p.status !== 'undefined') return
15-
if (val === p) throw new Error('TypeError: recurses! infinite promise chain detected')
16-
if (status && val && typeof val === 'object' && val.then) return val.then(resolve, reject)
17+
if (val === p) throw new Error('Error: recurses! infinite promise chain detected')
18+
if (status && val) try { if ('then' in val) return val.then(resolve, reject) } catch (e) {}
1719
p.status = status
1820
p.value = val
1921
flush()
2022
}
2123
}
2224

2325
function then (onResolve, onReject) {
24-
if (!onResolve && !onReject) return p
25-
if (!onResolve && p.status === true) return p
26-
if (!onReject && p.status === false) return p
2726
return chain(onResolve, onReject)
2827
}
2928

30-
function thenCatch (onReject) {
31-
return then(null, onReject)
32-
}
33-
3429
function chain (onResolve, onReject) {
3530
return promise(function (resolve, reject) {
3631
waiting.res.push(handleNext(onResolve))
3732
waiting.rej.push(handleNext(onReject))
3833
flush()
3934
function handleNext (handler) {
4035
return function next (value) {
41-
try { resolve(handler ? handler(value) : value) } catch (err) { reject(err) }
36+
try {
37+
value = handler ? handler(value) : value
38+
if (p.status) return resolve(value)
39+
return onReject ? resolve(value) : reject(value)
40+
} catch (err) { reject(err) }
4241
}
4342
}
4443
})

rejected.js reject.js

File renamed without changes.

resolved.js resolve.js

File renamed without changes.

test/test-a-plus.js

+6-11
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,10 @@
11
// // some of the spec is intentionally avoided, e.g.
22
// // a) the lib is capable of returning syncronously, i.e. it does not defer everything
33
// // b) to avoid bloat, the lib assumes non falsey args to be functions
4-
//
5-
// var resolved = require('../resolved')
6-
// var rejected = require('../rejected')
7-
// var deferred = require('../deferred')
8-
// var adapter = {
9-
// resolved: resolved,
10-
// rejected: rejected,
11-
// deferred: deferred
12-
// }
13-
// describe("Promises/A+ Tests", function () {
14-
// require("promises-aplus-tests").mocha(adapter)
4+
// describe('Promises/A+ Tests', function () {
5+
// require('promises-aplus-tests').mocha({
6+
// resolved: require('../resolve'),
7+
// rejected: require('../reject'),
8+
// deferred: require('../defer')
9+
// })
1510
// })

test/test-all.js

+13-23
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,44 @@
1-
/* globals describe it Promise */
1+
/* globals describe it */
22
var expect = require('chai').expect
3-
var promise = require('../index')
4-
var all = require('../all')
3+
var Promise = require('../extra')
54

65
describe('all', function () {
7-
describe('empty array', function () {
6+
describe('when called with an empty array', function () {
87
it('should return immediately', function () {
98
var values = []
109
var result
11-
all(values).then(function (val) {
10+
Promise.all(values).then(function (val) {
1211
result = val
1312
})
1413
expect(result).to.eql(values)
1514
})
1615
})
17-
describe('only values', function () {
16+
describe('when called with just values', function () {
1817
it('should return immediately', function () {
1918
var values = [1, 2, 3]
2019
var result
21-
all(values).then(function (val) {
20+
Promise.all(values).then(function (val) {
2221
result = val
2322
})
2423
expect(result).to.eql(values)
2524
})
2625
})
27-
describe('values and promises', function () {
28-
it('should replace promised values with the values themselves', function (done) {
29-
var values = [1, 2, later(3)]
30-
return all(values).then(function (val) {
26+
describe('when called with a mixture of values and promises', function () {
27+
it('should resolve the promises and return only values', function (done) {
28+
var values = [1, 2, Promise.resolve(3)]
29+
Promise.all(values).then(function (val) {
3130
expect(val).to.eql([1, 2, 3])
3231
done()
3332
})
3433
})
3534
})
3635
describe('error', function () {
3736
it('should reject the promise', function (done) {
38-
var values = [1, 2, later('error', true)]
39-
return all(values).catch(function (val) {
40-
expect(val).to.eql('error')
37+
var values = [1, 2, Promise.reject('error')]
38+
Promise.all(values).catch(function (err) {
39+
expect(err).to.eql('error')
4140
done()
4241
})
4342
})
4443
})
4544
})
46-
47-
function later (val, shouldReject) {
48-
return promise(function (resolve, reject) {
49-
setTimeout(function () {
50-
if (shouldReject) return reject(val)
51-
resolve(val)
52-
}, 0)
53-
})
54-
}

test/test-async-reject.js

+78-82
Original file line numberDiff line numberDiff line change
@@ -1,97 +1,93 @@
1-
/* globals describe it Promise */
1+
/* globals describe it */
22
var expect = require('chai').expect
33
var sinon = require('sinon')
4-
var promise = require('../index')
4+
var Promise = require('../extra')
55

66
describe('async reject', function () {
7-
it('should resolve values', function (done) {
8-
return promise(function (resolve, reject) {
9-
setTimeout(function () {
10-
reject(123)
11-
}, 0)
12-
})
13-
.then(null, function (val) {
14-
expect(val).to.eql(123)
15-
done()
16-
})
7+
it('should call reject', function () {
8+
var stub = sinon.stub()
9+
var d = Promise.defer()
10+
d.promise.then(null, stub)
11+
d.reject(123)
12+
expect(stub.calledWith(123)).to.eql(true)
1713
})
18-
it('should be chainable', function (done) {
19-
return promise(function (resolve, reject) {
20-
setTimeout(function () {
21-
reject(123)
22-
}, 0)
23-
})
24-
.then(null, function () {
25-
return 234
26-
})
27-
.then(function (chained) {
28-
expect(chained).to.eql(234)
29-
done()
30-
})
14+
it('should not call resolve', function () {
15+
var stub = sinon.stub()
16+
var d = Promise.defer()
17+
d.promise.then(stub, null)
18+
d.reject(123)
19+
expect(stub.called).to.eql(false)
3120
})
32-
it('should be really chainable', function (done) {
33-
return promise(function (resolve, reject) {
34-
setTimeout(function () {
35-
reject(123)
36-
}, 0)
37-
})
38-
.then(null, null)
39-
.then(null, function () {
40-
return 234
41-
})
42-
.then(function (chained) {
43-
expect(chained).to.eql(234)
44-
done()
45-
})
21+
it('should recover when caught', function () {
22+
var stub = sinon.stub()
23+
var d = Promise.defer()
24+
d.promise
25+
.then(null, function () { return 234 })
26+
.then(stub, null)
27+
d.reject(123)
28+
expect(stub.calledWith(234)).to.eql(true)
4629
})
47-
it('should not change once rejected', function (done) {
48-
return promise(function (resolve, reject) {
49-
setTimeout(function () {
50-
reject(123)
51-
reject(234)
52-
}, 0)
53-
})
54-
.then(null, function (val) {
55-
expect(val).to.eql(123)
56-
done()
57-
})
30+
it('should be really chainable', function () {
31+
var stub = sinon.stub()
32+
var d = Promise.defer()
33+
d.promise
34+
.then(null, null)
35+
.then(null, function () {
36+
return 234
37+
})
38+
.then(stub)
39+
d.reject(123)
40+
expect(stub.calledWith(234)).to.eql(true)
41+
})
42+
it('should be really really chainable', function () {
43+
var resolvedStub = sinon.stub()
44+
var rejectedStub = sinon.stub()
45+
var d = Promise.defer()
46+
d.promise
47+
.then(resolvedStub, null)
48+
.then(null, rejectedStub)
49+
d.reject(123)
50+
expect(resolvedStub.called).to.eql(false)
51+
expect(rejectedStub.calledWith(123)).to.eql(true)
5852
})
59-
it('should not call resolve', function (done) {
53+
it('should not change once rejected', function () {
6054
var stub = sinon.stub()
61-
return promise(function (resolve, reject) {
62-
setTimeout(function () {
63-
reject(123)
64-
}, 0)
65-
})
66-
.then(stub, function () {})
67-
.then(function () {
68-
expect(stub.called).to.eql(false)
69-
done()
70-
})
55+
var d = Promise.defer()
56+
d.promise.then(null, stub)
57+
d.reject(123)
58+
d.reject(234)
59+
expect(d.promise.value).to.eql(123)
7160
})
72-
73-
// requires native Promise api
74-
if (typeof Promise === 'undefined') return
75-
it('should not resolve promises', function (done) {
76-
return promise(function (resolve, reject) {
77-
setTimeout(function () {
78-
reject(Promise.resolve(123))
79-
}, 0)
80-
})
81-
.then(null, function (val) {
82-
expect(typeof val).to.eql('object')
83-
done()
61+
it('should reject an already fulfilled promise with the same reason', function () {
62+
var stub = sinon.stub()
63+
var d1 = Promise.defer()
64+
var d2 = Promise.defer()
65+
d1.promise.then(function () {
66+
return d2.promise
8467
})
68+
.then(null, stub)
69+
d1.resolve()
70+
d2.reject(123)
71+
expect(stub.calledWith(123)).to.eql(true)
8572
})
86-
it('should be compatible with the native promise api', function (done) {
87-
return Promise.all([promise(function (resolve, reject) {
88-
setTimeout(function () {
89-
reject(123)
90-
}, 0)
91-
})])
92-
.then(null, function (reason) {
93-
expect(reason).to.eql(123)
94-
done()
73+
it('should reject an already rejected promise from a rejected promise with the same reason', function () {
74+
var stub = sinon.stub()
75+
var d1 = Promise.defer()
76+
var d2 = Promise.defer()
77+
d1.promise.then(null, function () {
78+
return d2.promise
9579
})
80+
.then(null, stub)
81+
d1.reject()
82+
d2.reject(123)
83+
expect(stub.calledWith(123)).to.eql(true)
84+
})
85+
it('should not resolve promises', function () {
86+
var p = Promise.resolve(123)
87+
var stub = sinon.stub()
88+
var d = Promise.defer()
89+
d.promise.then(null, stub)
90+
d.reject(p)
91+
expect(stub.calledWith(p)).to.eql(true)
9692
})
9793
})

0 commit comments

Comments
 (0)