Skip to content

Commit

Permalink
Merge pull request #111 from fluture-js/avaq/done
Browse files Browse the repository at this point in the history
Add Future.done() and Future#done()
  • Loading branch information
Avaq committed Jun 1, 2017
2 parents 484b6f9 + 733c89b commit 2d09e89
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 0 deletions.
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ getPackageName('package.json')
1. [Consuming Futures](#consuming-futures)
* [fork](#fork)
* [value](#value)
* [done](#done)
* [promise](#promise)
1. [Parallelism](#parallelism)
* [race](#race)
Expand Down Expand Up @@ -828,6 +829,27 @@ Future.after(300, 'hello').value(console.log)();
//Nothing will happen. The Future was cancelled before it could settle.
```

#### done
##### `#done :: Future a b ~> Nodeback a b -> Cancel`
##### `.done :: Nodeback a b -> Future a b -> Cancel`

Fork the Future into a [Nodeback](#types).

```js
Future.of('hello').done((err, val) => console.log(val));
//> "hello"
```

This is like [fork](#fork), but instead of taking two unary functions, it takes
a single binary function. As with `fork()`, `done()` returns [`Cancel`](#types):

```js
const m = Future.after(300, 'hello');
const cancel = m.done((err, val) => console.log(val));
cancel();
//Nothing will happen. The Future was cancelled before it could settle.
```

#### promise
##### `#promise :: Future a b ~> Promise b a`
##### `.promise :: Future a b -> Promise b a`
Expand Down
7 changes: 7 additions & 0 deletions src/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,13 @@ Future.prototype.value = function Future$value(res){
return this._fork(throwRejection, res);
};

Future.prototype.done = function Future$done(callback){
if(!isFuture(this)) invalidContext('Future#done', this);
if(!isFunction(callback)) invalidArgument('Future#done', 0, 'to be a Function', callback);
return this._fork(function Future$done$rej(x){ callback(x) },
function Future$done$res(x){ callback(null, x) });
};

Future.prototype.promise = function Future$promise(){
return new Promise((res, rej) => this._fork(rej, res));
};
Expand Down
1 change: 1 addition & 0 deletions src/dispatchers.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export {race} from './dispatchers/race';
export {swap} from './dispatchers/swap';
export {fold} from './dispatchers/fold';

export {done} from './dispatchers/done';
export {fork} from './dispatchers/fork';
export {promise} from './dispatchers/promise';
export {value} from './dispatchers/value';
Expand Down
15 changes: 15 additions & 0 deletions src/dispatchers/done.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {isFuture} from '../core';
import {partial1} from '../internal/fn';
import {isFunction} from '../internal/is';
import {invalidArgument, invalidFuture} from '../internal/throw';

function done$callback(callback, m){
if(!isFuture(m)) invalidFuture('Future.done', 1, m);
return m.done(callback);
}

export function done(callback, m){
if(!isFunction(callback)) invalidArgument('Future.done', 0, 'be a Function', callback);
if(arguments.length === 1) return partial1(done$callback, callback);
return done$callback(callback, m);
}
73 changes: 73 additions & 0 deletions test/1.future.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
isFuture,
fork,
value,
done,
promise,
seq,
Par,
Expand Down Expand Up @@ -103,6 +104,36 @@ describe('Future', () => {

});

describe('.done()', () => {

it('is a curried binary function', () => {
expect(done).to.be.a('function');
expect(done.length).to.equal(2);
expect(done(U.noop)).to.be.a('function');
});

it('throws when not given a Function as first argument', () => {
const f = () => done(1);
expect(f).to.throw(TypeError, /Future.*first/);
});

it('throws when not given a Future as second argument', () => {
const f = () => done(U.add(1), 1);
expect(f).to.throw(TypeError, /Future.*second/);
});

it('dispatches to #done()', fin => {
const a = () => {};
const mock = Object.create(F.mock);
mock.done = x => {
expect(x).to.equal(a);
fin();
};
done(a, mock);
});

});

describe('.promise()', () => {

it('throws when not given a Future', () => {
Expand Down Expand Up @@ -239,6 +270,48 @@ describe('Future', () => {

});

describe('#done()', () => {

it('throws when invoked out of context', () => {
const f = () => F.mock.done.call(null, U.noop);
expect(f).to.throw(TypeError, /Future/);
});

it('throws TypeError when not given a function', () => {
const xs = [NaN, {}, [], 1, 'a', new Date, undefined, null];
const fs = xs.map(x => () => F.mock.done(x));
fs.forEach(f => expect(f).to.throw(TypeError, /Future/));
});

it('passes the rejection value as first parameter', fin => {
const mock = Object.create(Future.prototype);
mock._fork = l => {l(1)};
mock.done((x, y) => {
expect(x).to.equal(1);
expect(y).to.equal(undefined);
fin();
});
});

it('passes the resolution value as second parameter', fin => {
const mock = Object.create(Future.prototype);
mock._fork = (l, r) => {r(1)};
mock.done((x, y) => {
expect(x).to.equal(null);
expect(y).to.equal(1);
fin();
});
});

it('returns the return done of #_fork()', () => {
const mock = Object.create(Future.prototype);
const sentinel = {};
mock._fork = () => sentinel;
expect(mock.done(U.noop)).to.equal(sentinel);
});

});

describe('#promise()', () => {

it('returns a Promise', () => {
Expand Down

0 comments on commit 2d09e89

Please sign in to comment.