Comparing version 0.4.0 to 0.5.0
@@ -6,3 +6,3 @@ { | ||
"authors": ["Paul Mucur"], | ||
"version": "0.4.0", | ||
"version": "0.5.0", | ||
"main": "lib/pacta.js", | ||
@@ -9,0 +9,0 @@ "devDependencies": { |
114
lib/pacta.js
@@ -169,3 +169,3 @@ /*global module, define, process, setImmediate, setTimeout */ | ||
/* onRejected :: Promise a -> (a -> b) -> Promise b */ | ||
/* onRejected :: Promise e a -> (e -> a) -> Promise a */ | ||
Promise.prototype.onRejected = function (f) { | ||
@@ -198,5 +198,6 @@ var promise = new Promise(), | ||
/* map :: Promise a -> (a -> b) -> Promise b */ | ||
/* map :: Promise e a -> (a -> b) -> Promise e b */ | ||
Promise.prototype.map = function (f) { | ||
var promise = new Promise(), | ||
reason = this.reason, | ||
value = this.value; | ||
@@ -212,2 +213,6 @@ | ||
}); | ||
} else if (this.rejected) { | ||
nextTick(function () { | ||
promise.reject(reason); | ||
}); | ||
} else { | ||
@@ -223,2 +228,8 @@ this.emitter.once('resolved', function (x) { | ||
}); | ||
this.emitter.once('rejected', function (x) { | ||
nextTick(function () { | ||
promise.reject(x); | ||
}); | ||
}); | ||
} | ||
@@ -229,3 +240,42 @@ | ||
/* chain :: Promise a -> (a -> Promise b) -> Promise b */ | ||
/* mapError :: Promise e a -> (e -> f) -> Promise f a */ | ||
Promise.prototype.mapError = function (f) { | ||
var promise = new Promise(), | ||
reason = this.reason, | ||
value = this.value; | ||
if (this.resolved) { | ||
nextTick(function () { | ||
promise.resolve(value); | ||
}); | ||
} else if (this.rejected) { | ||
nextTick(function () { | ||
try { | ||
promise.reject(f(reason)); | ||
} catch (e) { | ||
promise.reject(e); | ||
} | ||
}); | ||
} else { | ||
this.emitter.once('resolved', function (x) { | ||
nextTick(function () { | ||
promise.resolve(x); | ||
}); | ||
}); | ||
this.emitter.once('rejected', function (x) { | ||
nextTick(function () { | ||
try { | ||
promise.reject(f(x)); | ||
} catch (e) { | ||
promise.reject(e); | ||
} | ||
}); | ||
}); | ||
} | ||
return promise; | ||
}; | ||
/* chain :: Promise e a -> (a -> Promise e b) -> Promise e b */ | ||
Promise.prototype.chain = function (f) { | ||
@@ -240,12 +290,42 @@ var promise = new Promise(); | ||
*/ | ||
this.map(f).map(function (x) { | ||
x.map(function (y) { | ||
promise.resolve(y); | ||
this.map(f).map(function (pb) { | ||
pb.map(function (value) { | ||
promise.resolve(value); | ||
}); | ||
pb.onRejected(function (reason) { | ||
promise.reject(reason); | ||
}); | ||
}); | ||
this.onRejected(function (reason) { | ||
promise.reject(reason); | ||
}); | ||
return promise; | ||
}; | ||
/* concat :: Promise a -> Promise a */ | ||
/* chainError :: Promise e a -> (e -> Promise f a) -> Promise f a */ | ||
Promise.prototype.chainError = function (f) { | ||
var promise = new Promise(); | ||
/* Same algorithm as above */ | ||
this.mapError(f).mapError(function (pb) { | ||
pb.map(function (value) { | ||
promise.resolve(value); | ||
}); | ||
pb.onRejected(function (reason) { | ||
promise.reject(reason); | ||
}); | ||
}); | ||
this.map(function(value) { | ||
promise.resolve(value); | ||
}); | ||
return promise; | ||
}; | ||
/* concat :: Promise e a -> Promise e a -> Promise e a */ | ||
Promise.prototype.concat = function (other) { | ||
@@ -271,3 +351,3 @@ var promise = new Promise(); | ||
/* ap :: Promise (a -> b) -> Promise a -> Promise b */ | ||
/* ap :: Promise e (a -> b) -> Promise e a -> Promise e b */ | ||
Promise.prototype.ap = function (m) { | ||
@@ -279,3 +359,3 @@ return this.chain(function (f) { | ||
/* empty :: Promise a -> Promise a */ | ||
/* empty :: Promise e a -> Promise e a */ | ||
Promise.prototype.empty = function () { | ||
@@ -285,6 +365,6 @@ return Promise.of(this.value.empty ? this.value.empty() : this.value.constructor.empty()); | ||
/* conjoin :: Promise a -> Promise b -> Promise [a b] | ||
* conjoin :: Promise a -> Promise [b] -> Promise [a b] | ||
* conjoin :: Promise [a] -> Promise b -> Promise [a b] | ||
* conjoin :: Promise [a] -> Promise [b] -> Promise [a b] | ||
/* conjoin :: Promise e a -> Promise e b -> Promise e [a b] | ||
* conjoin :: Promise e a -> Promise e [b] -> Promise e [a b] | ||
* conjoin :: Promise e [a] -> Promise e b -> Promise e [a b] | ||
* conjoin :: Promise e [a] -> Promise e [b] -> Promise e [a b] | ||
*/ | ||
@@ -297,3 +377,3 @@ Promise.prototype.conjoin = function (other) { | ||
/* append :: Promise [a] -> Promise b -> Promise [a b] */ | ||
/* append :: Promise e [a] -> Promise e b -> Promise e [a b] */ | ||
Promise.prototype.append = function (other) { | ||
@@ -305,3 +385,3 @@ return this.concat(other.map(function (x) { | ||
/* spread :: Promise a -> (a -> b) -> Promise b */ | ||
/* spread :: Promise e [a b] -> (a -> b -> c) -> Promise e c */ | ||
Promise.prototype.spread = Promise.prototype.explode = function (f) { | ||
@@ -313,2 +393,3 @@ return this.map(function (x) { | ||
/* reduce :: Promise e [a] -> (b -> a -> b) -> b -> Promise e b */ | ||
Promise.prototype.reduce = function () { | ||
@@ -323,2 +404,3 @@ var args = [].slice.call(arguments); | ||
/* Compatibility with the Promises/A+ specification. */ | ||
/* then :: Promise e a -> (a -> b) -> (e -> b) -> Promise e b */ | ||
Promise.prototype.then = function (onFulfilled, onRejected) { | ||
@@ -411,3 +493,3 @@ var promise = new Promise(); | ||
/* of :: a -> Promise a */ | ||
/* of :: a -> Promise e a */ | ||
Promise.of = function (x) { | ||
@@ -414,0 +496,0 @@ return new Promise(x); |
@@ -7,3 +7,3 @@ { | ||
"keywords": ["promises", "monad", "functor", "promises-aplus"], | ||
"version": "0.4.0", | ||
"version": "0.5.0", | ||
"main": "./lib/pacta.js", | ||
@@ -10,0 +10,0 @@ "dependencies": {}, |
# pacta [![Build Status](https://travis-ci.org/mudge/pacta.png?branch=master)](https://travis-ci.org/mudge/pacta) | ||
```javascript | ||
{ 'pacta': '0.4.0' } | ||
{ 'pacta': '0.5.0' } | ||
``` | ||
@@ -244,3 +244,3 @@ | ||
```haskell | ||
map :: Promise a -> (a -> b) -> Promise b | ||
map :: Promise e a -> (a -> b) -> Promise e b | ||
``` | ||
@@ -256,2 +256,28 @@ | ||
### `Promise#mapError(f)` | ||
```javascript | ||
var promise = new Promise(); | ||
promise.reject("Type error at line 214"); | ||
promise.mapError(function (x) { | ||
console.log(x); | ||
return "Error: " + x; | ||
}); //=> Rejected promise with "Error: Type error at line 214" as reason | ||
``` | ||
Execute a function `f` on the reason of a rejected promise. This returns a new | ||
rejected promise containing the result of applying `f` to the initial | ||
promise's reason. | ||
In [Haskell](http://www.haskell.org) notation, its type signature is: | ||
```haskell | ||
mapError :: Promise e a -> (e -> f) -> Promise f a | ||
``` | ||
Note that any uncaught exceptions during the execution of `f` will result in | ||
the promise being `rejected` with the exception as its `reason`. | ||
### `Promise#then([onFulfilled[, onRejected]])` | ||
@@ -341,3 +367,3 @@ | ||
```haskell | ||
concat :: Promise a -> Promise a -> Promise a | ||
concat :: Promise e a -> Promise e a -> Promise e a | ||
``` | ||
@@ -367,5 +393,51 @@ | ||
```haskell | ||
chain :: Promise a -> (a -> Promise b) -> Promise b | ||
chain :: Promise e a -> (a -> Promise e b) -> Promise e b | ||
``` | ||
### `Promise#chainError(f)` | ||
```javascript | ||
var criticalAjaxCallThatMayFail = function() { | ||
var p = new Promise(); | ||
setTimeout(function() { | ||
if(Date.now() % 2 == 0) { | ||
p.reject("Request timed out"); | ||
} | ||
else { | ||
p.resolve("This is a critical sentence."); | ||
} | ||
}, 2000); | ||
return p; | ||
}; | ||
var getMessage = function(error) { | ||
if(error) { | ||
console.error("Error received: " + error); | ||
console.log("Retrying…"); | ||
} | ||
console.log("Sending request…"); | ||
return criticalAjaxCallThatMayFail(); | ||
}; | ||
/* Retry 2 times if it fails */ | ||
getMessage() | ||
.chainError(getMessage) | ||
.chainError(getMessage) | ||
.map(console.log) | ||
.mapError(console.error); | ||
``` | ||
Execute a function `f` with the reason of a rejected promise. This differs | ||
from [`Promise#mapError`](#promisemaperrorf) in that the function *must* | ||
return a promise itself. | ||
Its type signature is: | ||
```haskell | ||
chainError :: Promise e a -> (e -> Promise f a) -> Promise f a | ||
``` | ||
### `Promise#ap(p)` | ||
@@ -386,3 +458,3 @@ | ||
```haskell | ||
ap :: Promise (a -> b) -> Promise a -> Promise b | ||
ap :: Promise e (a -> b) -> Promise e a -> Promise e b | ||
``` | ||
@@ -467,2 +539,7 @@ | ||
## Contributors | ||
`mapError` and `chainError` were contributed by [Rodolphe | ||
Belouin](https://github.com/rbelouin). | ||
## Acknowledgements | ||
@@ -469,0 +546,0 @@ |
@@ -14,3 +14,3 @@ /*global describe, it, beforeEach, require, setTimeout */ | ||
describe('Promise', function () { | ||
var p, p2, p3, p4; | ||
var p, p2, p3, p4, p5, p6, p7; | ||
@@ -32,2 +32,17 @@ beforeEach(function () { | ||
}, 75); | ||
p5 = new Promise(); | ||
setTimeout(function () { | ||
p5.reject('foo'); | ||
}, 50); | ||
p6 = new Promise(); | ||
setTimeout(function () { | ||
p6.reject('bar'); | ||
}, 25); | ||
p7 = new Promise(); | ||
setTimeout(function () { | ||
p7.reject('baz'); | ||
}, 25); | ||
}); | ||
@@ -283,2 +298,90 @@ | ||
describe('#mapError', function () { | ||
it('yields the reason of the promise', function (done) { | ||
p5.mapError(function (x) { | ||
assert.equal('foo', x); | ||
done(); | ||
}); | ||
}); | ||
it('yields the reason after rejection', function (done) { | ||
p5.mapError(function () { | ||
/* Promise is now rejected so mapError again... */ | ||
p5.mapError(function (x) { | ||
assert.equal('foo', x); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
it('can be chained', function (done) { | ||
p5.mapError(function (x) { | ||
return x + '!'; | ||
}).mapError(function (y) { | ||
assert.equal('foo!', y); | ||
done(); | ||
}); | ||
}); | ||
it('can be nested', function (done) { | ||
p5.mapError(function (x) { | ||
p6.mapError(function (y) { | ||
p7.mapError(function (z) { | ||
assert.equal('foo', x); | ||
assert.equal('bar', y); | ||
assert.equal('baz', z); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
}); | ||
it('encapsulates exceptions in rejections', function (done) { | ||
var exception = new TypeError(), | ||
pe1 = new Promise(), | ||
pe2; | ||
pe1.reject('foo'); | ||
pe2 = pe1.mapError(function () { | ||
throw exception; | ||
}); | ||
pe2.onRejected(function (r) { | ||
assert.equal('rejected', pe2.state()); | ||
assert.equal(exception, r); | ||
done(); | ||
}); | ||
}); | ||
it('fulfils the identity property of a functor', function (done) { | ||
p5.mapError(function (x) { | ||
return x; | ||
}).mapError(function (x) { | ||
assert.equal('foo', x); | ||
done(); | ||
}); | ||
}); | ||
it('fulfils the composition property of a functor #1', function (done) { | ||
var f = function (x) { return 'f(' + x + ')'; }, | ||
g = function (x) { return 'g(' + x + ')'; }; | ||
p5.mapError(function (x) { return f(g(x)); }).mapError(function (x) { | ||
assert.equal('f(g(foo))', x); | ||
done(); | ||
}); | ||
}); | ||
it('fulfils the composition property of a functor #2', function (done) { | ||
var f = function (x) { return 'f(' + x + ')'; }, | ||
g = function (x) { return 'g(' + x + ')'; }; | ||
p5.mapError(g).mapError(f).mapError(function (x) { | ||
assert.equal('f(g(foo))', x); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
describe('#then', function () { | ||
@@ -442,2 +545,24 @@ it('yields its value like #map', function (done) { | ||
describe('#chainError', function () { | ||
it('fulfils the associativity property of chain #1', function (done) { | ||
var f = function (x) { var p = new Promise(); p.reject('f(' + x + ')'); return p; }, | ||
g = function (x) { var p = new Promise(); p.reject('g(' + x + ')'); return p; }; | ||
p5.chainError(f).chainError(g).mapError(function (x) { | ||
assert.equal('g(f(foo))', x); | ||
done(); | ||
}); | ||
}); | ||
it('fulfils the associativity property of chain #2', function (done) { | ||
var f = function (x) { var p = new Promise(); p.reject('f(' + x + ')'); return p; }, | ||
g = function (x) { var p = new Promise(); p.reject('g(' + x + ')'); return p; }; | ||
p5.chainError(function (x) { return f(x).chainError(g); }).mapError(function (x) { | ||
assert.equal('g(f(foo))', x); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
describe('#ap', function () { | ||
@@ -444,0 +569,0 @@ it('fulfils the identity property of applicative', function (done) { |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
90741
1203
556