Skip to content

Commit

Permalink
Merge pull request #9 from mudge/chain-error-exceptions
Browse files Browse the repository at this point in the history
Encapsulate exceptions during chain and chainError
  • Loading branch information
mudge committed Mar 11, 2015
2 parents 3078abf + 6ee76c1 commit 8c131fa
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 21 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -532,8 +532,10 @@ list to the function separately.

## Contributors

`mapError` and `chainError` were contributed by [Rodolphe
Belouin](https://github.com/rbelouin).
* Fixes to `chain`, `chainError` and `empty` were contributed by [Ben
Schulz](https://github.com/benschulz);
* `mapError` and `chainError` were contributed by [Rodolphe
Belouin](https://github.com/rbelouin).

## Acknowledgements

Expand Down
50 changes: 31 additions & 19 deletions lib/pacta.js
Original file line number Diff line number Diff line change
Expand Up @@ -276,20 +276,25 @@
Promise.prototype.chain = function (f) {
var promise = new Promise();

/* Map over the given Promise a with (a -> Promise b), returning a new
* Promise (Promise b). Map over that, thereby gaining access to the inner
/* Map over the given Promise a calling (a -> Promise b) to return a new
* Promise b. Map over that in order to get to the inner value of b and
* resolve another promise with it. Return that promise as it is equivalent
* to Promise b.
*/
this.map(f).map(function (pb) {
pb.map(function (value) {
promise.resolve(value);
});
this.map(function (value) {
try {
var pb = f(value);

pb.onRejected(function (reason) {
promise.reject(reason);
});
pb.map(function (value) {
promise.resolve(value);
});

pb.onRejected(function (reason) {
promise.reject(reason);
});
} catch (e) {
promise.reject(e);
}
});

this.onRejected(function (reason) {
Expand All @@ -303,18 +308,23 @@
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);
});
this.mapError(function (reason) {
try {
var pb = f(reason);

pb.onRejected(function (reason) {
promise.reject(reason);
});
pb.map(function (value) {
promise.resolve(value);
});

pb.onRejected(function (reason) {
promise.reject(reason);
});
} catch (e) {
promise.reject(e);
}
});

this.map(function(value) {
this.map(function (value) {
promise.resolve(value);
});

Expand Down Expand Up @@ -351,7 +361,9 @@

/* empty :: Promise e a -> Promise e a */
Promise.prototype.empty = function () {
return Promise.of(this.value.empty ? this.value.empty() : this.value.constructor.empty());
return this.map(function (value) {
return value.empty ? value.empty() : value.constructor.empty();
});
};

/* conjoin :: Promise e a -> Promise e b -> Promise e [a b]
Expand Down
53 changes: 53 additions & 0 deletions test/pacta_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,27 @@
done();
});
});

it('encapsulates exceptions in rejections', function (done) {
var exception = new TypeError(),
p = fulfilledPromise().chain(function () { throw exception; });

p.onRejected(function (r) {
assert.equal('rejected', p.state());
assert.equal(exception, r);
done();
});
});

it('rejects the returned promise, if f does not return a promise', function (done) {
var p = fulfilledPromise().chain(function () { return 'not-a-promise'; });

p.onRejected(function (r) {
assert.equal('rejected', p.state());
assert.equal(TypeError, r.constructor);
done();
});
});
});

describe('#chainError', function () {
Expand All @@ -558,6 +579,27 @@
done();
});
});

it('encapsulates exceptions in rejections', function (done) {
var exception = new TypeError(),
p = rejectedPromise().chainError(function () { throw exception; });

p.onRejected(function (r) {
assert.equal('rejected', p.state());
assert.equal(exception, r);
done();
});
});

it('rejects the returned promise, if f does not return a promise', function (done) {
var p = rejectedPromise().chainError(function () { return 'not-a-promise'; });

p.onRejected(function (r) {
assert.equal('rejected', p.state());
assert.equal(TypeError, r.constructor);
done();
});
});
});

describe('#ap', function () {
Expand Down Expand Up @@ -655,6 +697,17 @@
done();
});
});

it('works with unresolved promises', function (done) {
var p = emptyPromise();

p.concat(p.empty()).map(function (x) {
assert.deepEqual([1], x);
done();
});

p.resolve([1]);
});
});

describe('#conjoin', function () {
Expand Down

0 comments on commit 8c131fa

Please sign in to comment.