Comparing version 0.6.0-beta2 to 0.6.1
277
fluture.js
@@ -23,12 +23,2 @@ /*global define FantasyLand inspectf*/ | ||
///////////// | ||
// Helpers // | ||
///////////// | ||
function noop(){} | ||
function fork(m, rej, res){ | ||
const clean = m._f(rej, res); | ||
return typeof clean === 'function' ? clean : noop; | ||
} | ||
/////////////////// | ||
@@ -54,6 +44,2 @@ // Type checking // | ||
function isBoolean(b){ | ||
return typeof b === 'boolean'; | ||
} | ||
//////////////////// | ||
@@ -104,5 +90,4 @@ // Error handling // | ||
function check$Future(f, b){ | ||
if(!isFunction(f)) error$invalidArgument('Future', 0, 'be a function', f); | ||
if(!isBoolean(b)) error$invalidArgument('Future', 0, 'be a boolean', b); | ||
function check$Future(fork){ | ||
if(!isFunction(fork)) error$invalidArgument('Future', 0, 'be a function', fork); | ||
} | ||
@@ -150,2 +135,3 @@ | ||
//Check resolution value of the Future on which #ap was called. | ||
function check$ap$f(f){ | ||
@@ -187,8 +173,8 @@ if(!isFunction(f)) throw new TypeError( | ||
function check$cache$settle(oldState, newState, oldValue, newValue){ | ||
if(oldState !== 'pending') throw new Error( | ||
'A cached Future may only resolve or reject once;' | ||
+ ` tried to go into a ${newState} state while already ${oldState}.` | ||
if(oldState > 1) throw new Error( | ||
'Future.cache expects the Future it wraps to only resolve or reject once; ' | ||
+ ' a cached Future tried to ' + (newState === 2 ? 'reject' : 'resolve') + ' a second time.' | ||
+ ' Please check your cached Future and make sure it does not call res or rej multiple times' | ||
+ `\n It was ${oldState} with: ${show(oldValue)}` | ||
+ `\n It got ${newState} with: ${show(newValue)}` | ||
+ '\n It was ' + (oldState === 2 ? 'rejected' : 'resolved') + ' with: ' + show(oldValue) | ||
+ '\n It got ' + (newState === 2 ? 'rejected' : 'resolved') + ' with: ' + show(newValue) | ||
); | ||
@@ -229,13 +215,14 @@ } | ||
function FutureClass(f, b){ | ||
//Constructor. | ||
function FutureClass(f){ | ||
this._f = f; | ||
this._b = b; | ||
} | ||
function Future(f, b){ | ||
if(arguments.length < 2) b = true; | ||
check$Future(f, b); | ||
return new FutureClass(f, b); | ||
//A createFuture function which pretends to be Future. | ||
function Future(f){ | ||
check$Future(f); | ||
return new FutureClass(f); | ||
} | ||
//The of method. | ||
function Future$of(x){ | ||
@@ -249,13 +236,3 @@ return new FutureClass(function Future$of$fork(rej, res){ | ||
check$fork(this, rej, res); | ||
let immediate = false, clear; | ||
const autoclear = this._b; | ||
clear = fork(this, function Future$fork$rej(x){ | ||
rej(x); | ||
autoclear && clear ? clear() : (immediate = true); | ||
}, function Future$fork$res(x){ | ||
res(x); | ||
autoclear && clear ? clear() : (immediate = true); | ||
}); | ||
autoclear && immediate && clear(); | ||
return clear; | ||
this._f(rej, res); | ||
} | ||
@@ -267,17 +244,8 @@ | ||
return new FutureClass(function Future$chain$fork(rej, res){ | ||
let cleared = false, clearThat = noop; | ||
const clearThis = fork(_this, function Future$chain$rej(x){ | ||
cleared || rej(x); | ||
}, function Future$chain$res(x){ | ||
if(cleared) return; | ||
_this._f(rej, function Future$chain$res(x){ | ||
const m = f(x); | ||
check$chain$f(m, f, x); | ||
clearThat = fork(m, e => cleared || rej(e), x => cleared || res(x)); | ||
m._f(rej, res); | ||
}); | ||
return function Future$chain$clear(){ | ||
cleared = true; | ||
clearThis(); | ||
clearThat(); | ||
}; | ||
}, this._b); | ||
}); | ||
} | ||
@@ -289,17 +257,8 @@ | ||
return new FutureClass(function Future$chainRej$fork(rej, res){ | ||
let cleared = false, clearThat = noop; | ||
const clearThis = fork(_this, function Future$chainRej$rej(x){ | ||
if(cleared) return; | ||
_this._f(function Future$chainRej$rej(x){ | ||
const m = f(x); | ||
check$chainRej$f(m, f, x); | ||
clearThat = fork(m, e => cleared || rej(e), x => cleared || res(x)); | ||
}, function Future$chainRej$res(x){ | ||
cleared || res(x); | ||
}); | ||
return function Future$chainRej$clear(){ | ||
cleared = true; | ||
clearThis(); | ||
clearThat(); | ||
}; | ||
}, _this._b); | ||
m._f(rej, res); | ||
}, res); | ||
}); | ||
} | ||
@@ -311,13 +270,6 @@ | ||
return new FutureClass(function Future$map$fork(rej, res){ | ||
let cleared = false; | ||
const clear = fork(_this, function Future$map$rej(x){ | ||
cleared || rej(x); | ||
}, function Future$map$res(x){ | ||
cleared || res(f(x)); | ||
_this._f(rej, function Future$map$res(x){ | ||
res(f(x)); | ||
}); | ||
return function Future$map$clear(){ | ||
cleared = true; | ||
clear(); | ||
}; | ||
}, _this._b); | ||
}); | ||
} | ||
@@ -329,6 +281,5 @@ | ||
return new FutureClass(function Future$ap$fork(g, res){ | ||
let _f, _x, ok1, ok2, ko, cleared = false; | ||
const rej = x => cleared || ko || (ko = 1, g(x)); | ||
const clearThis = fork(_this, rej, function Future$ap$resThis(f){ | ||
if(cleared) return; | ||
let _f, _x, ok1, ok2, ko; | ||
const rej = x => ko || (ko = 1, g(x)); | ||
_this._f(rej, function Future$ap$resThis(f){ | ||
if(!ok2) return void (ok1 = 1, _f = f); | ||
@@ -338,4 +289,3 @@ check$ap$f(f); | ||
}); | ||
const clearThat = fork(m, rej, function Future$ap$resThat(x){ | ||
if(cleared) return; | ||
m._f(rej, function Future$ap$resThat(x){ | ||
if(!ok1) return void (ok2 = 1, _x = x) | ||
@@ -345,8 +295,3 @@ check$ap$f(_f); | ||
}); | ||
return function Future$ap$clear(){ | ||
cleared = true; | ||
clearThis(); | ||
clearThat(); | ||
}; | ||
}, _this._b); | ||
}); | ||
} | ||
@@ -362,12 +307,7 @@ | ||
return new FutureClass(function Future$race$fork(rej, res){ | ||
let settled = false, cleared = false; | ||
const once = f => a => cleared || settled || (settled = true, f(a)); | ||
const clearThis = fork(_this, once(rej), once(res)); | ||
const clearThat = fork(m, once(rej), once(res)); | ||
return function Future$race$clear(){ | ||
cleared = true; | ||
clearThis(); | ||
clearThat(); | ||
}; | ||
}, _this._b); | ||
let settled = false; | ||
const once = f => a => settled || (settled = true, f(a)); | ||
_this._f(once(rej), once(res)); | ||
m._f(once(rej), once(res)); | ||
}); | ||
} | ||
@@ -379,17 +319,12 @@ | ||
return new FutureClass(function Future$or$fork(rej, res){ | ||
let ok = false, ko = false, val, err, cleared = false; | ||
const clearThis = fork(_this, | ||
() => cleared || ko ? rej(err) : ok ? res(val) : (ko = true), | ||
x => cleared || (ok = true, res(x)) | ||
let ok = false, ko = false, val, err; | ||
_this._f( | ||
() => ko ? rej(err) : ok ? res(val) : (ko = true), | ||
x => (ok = true, res(x)) | ||
); | ||
const clearThat = fork(m, | ||
e => cleared || ok || (ko ? rej(e) : (err = e, ko = true)), | ||
x => cleared || ok || (ko ? res(x) : (val = x, ok = true)) | ||
m._f( | ||
e => ok || (ko ? rej(e) : (err = e, ko = true)), | ||
x => ok || (ko ? res(x) : (val = x, ok = true)) | ||
); | ||
return function Future$or$clear(){ | ||
cleared = true; | ||
clearThis(); | ||
clearThat(); | ||
}; | ||
}, _this._b); | ||
}); | ||
} | ||
@@ -401,13 +336,4 @@ | ||
return new FutureClass(function Future$fold$fork(rej, res){ | ||
let cleared = false; | ||
const clear = fork(_this, function Future$fold$rej(e){ | ||
cleared || res(f(e)); | ||
}, function Future$fold$res(x){ | ||
cleared || res(g(x)); | ||
}); | ||
return function Future$fold$clear(){ | ||
cleared = true; | ||
clear(); | ||
}; | ||
}, _this._b); | ||
_this._f(e => res(f(e)), x => res(g(x))); | ||
}); | ||
} | ||
@@ -417,3 +343,3 @@ | ||
check$value(this, f); | ||
return this.fork( | ||
this._f( | ||
function Future$value$rej(e){ | ||
@@ -439,5 +365,5 @@ throw new Error( | ||
const _this = this; | ||
let que = [], value, state = 'idle'; | ||
let que = []; | ||
let value, state; | ||
const settleWith = newState => function Future$cache$settle(newValue){ | ||
if(state === 'idle') return; | ||
check$cache$settle(state, newState, value, newValue); | ||
@@ -452,19 +378,12 @@ value = newValue; state = newState; | ||
return new FutureClass(function Future$cache$fork(rej, res){ | ||
let clear = noop; | ||
switch(state){ | ||
case 'pending': que.push({rejected: rej, resolved: res}); break; | ||
case 'rejected': rej(value); break; | ||
case 'resolved': res(value); break; | ||
case 'idle': | ||
state = 'pending'; | ||
que.push({rejected: rej, resolved: res}); | ||
clear = fork(_this, settleWith('rejected'), settleWith('resolved')); | ||
case 1: que.push({2: rej, 3: res}); break; | ||
case 2: rej(value); break; | ||
case 3: res(value); break; | ||
default: | ||
state = 1; | ||
que.push({2: rej, 3: res}); | ||
_this.fork(settleWith(2), settleWith(3)); | ||
} | ||
return function Future$cache$clear(){ | ||
state = 'idle'; | ||
value = undefined; | ||
que = []; | ||
clear(); | ||
}; | ||
}, false); | ||
}); | ||
} | ||
@@ -532,19 +451,39 @@ | ||
//chain :: Chain m => (a -> m b) -> m a -> m b | ||
Future.chain = createUnaryDispatcher('chain'); | ||
//chainRej :: (a -> Future a c) -> Future a b -> Future a c | ||
Future.chainRej = createUnaryDispatcher('chainRej'); | ||
//map :: Functor m => (a -> b) -> m a -> m b | ||
Future.map = createUnaryDispatcher('map'); | ||
//ap :: Apply m => m (a -> b) -> m a -> m b | ||
Future.ap = function dispatch$ap(m, a){ | ||
if(arguments.length === 1) return a => dispatch$ap(m, a); | ||
if(m && typeof m.ap === 'function') return m.ap(a); | ||
error$invalidArgument('Future.ap', 0, 'have a "ap" method', m); | ||
}; | ||
//fork :: (a -> Void) -> (b -> Void) -> Future a b -> Void | ||
Future.fork = createBinaryDispatcher('fork'); | ||
//race :: Future a b -> Future a b -> Future a b | ||
Future.race = createUnaryDispatcher('race'); | ||
//or :: Future a b -> Future a b -> Future a b | ||
Future.or = createUnaryDispatcher('or'); | ||
//fold :: (a -> c) -> (b -> c) -> Future a b -> Future _ c | ||
Future.fold = createBinaryDispatcher('fold'); | ||
//value :: (b -> Void) -> Future a b -> Void | ||
Future.value = createUnaryDispatcher('value'); | ||
//promise :: Future a b -> Promise b a | ||
Future.promise = createNullaryDispatcher('promise'); | ||
//cache :: Future a b -> Future a b | ||
Future.cache = createNullaryDispatcher('cache'); | ||
Future.ap = function dispatch$ap(m, a){ | ||
if(arguments.length === 1) return a => dispatch$ap(m, a); | ||
if(m && typeof m.ap === 'function') return m.ap(a); | ||
error$invalidArgument('Future.ap', 0, 'have a "ap" method', m); | ||
}; | ||
////////////////// | ||
@@ -554,2 +493,3 @@ // Constructors // | ||
//Create a Future which rejects witth the given value. | ||
Future.reject = function Future$reject(x){ | ||
@@ -561,2 +501,3 @@ return new FutureClass(function Future$reject$fork(rej){ | ||
//Create a Future which resolves after the given time with the given value. | ||
Future.after = function Future$after(n, x){ | ||
@@ -566,6 +507,3 @@ if(arguments.length === 1) return x => Future$after(n, x); | ||
return new FutureClass(function Future$after$fork(rej, res){ | ||
const id = setTimeout(res, n, x); | ||
return function Future$after$clear(){ | ||
clearTimeout(id); | ||
}; | ||
setTimeout(res, n, x); | ||
}) | ||
@@ -575,17 +513,12 @@ }; | ||
Future.cast = function Future$cast(m){ | ||
check$cast(m); | ||
if(m instanceof FutureClass){ | ||
return m; | ||
} | ||
check$cast(m); | ||
return new FutureClass(function Future$cast$fork(rej, res){ | ||
let cleared = false; | ||
m.fork(function Future$cast$rej(x){ | ||
cleared || rej(x); | ||
}, function Future$cast$res(x){ | ||
cleared || res(x); | ||
}); | ||
return () => (cleared = true); | ||
m.fork(rej, res); | ||
}); | ||
}; | ||
//encase :: (a -> !b | c) -> a -> Future b c | ||
Future.encase = function Future$encase(f, x){ | ||
@@ -606,2 +539,4 @@ if(arguments.length === 1) return x => Future$encase(f, x); | ||
//Create a Future which resolves with the return value of the given function, | ||
//or rejects with the exception thrown by the given function. | ||
Future.try = function Future$try(f){ | ||
@@ -611,11 +546,11 @@ return Future.encase(f, undefined); | ||
//node :: ((err, a) -> Void) -> Future[Error, a] | ||
Future.node = function Future$node(f){ | ||
check$node(f); | ||
return new FutureClass(function Future$node$fork(rej, res){ | ||
let cleared = false; | ||
f((a, b) => cleared || (a ? rej(a) : res(b))); | ||
return () => (cleared = true); | ||
f((a, b) => a ? rej(a) : res(b)); | ||
}); | ||
}; | ||
//parallel :: PositiveInteger -> [Future a b] -> Future a [b] | ||
Future.parallel = function Future$parallel(i, ms){ | ||
@@ -626,17 +561,11 @@ if(arguments.length === 1) return ms => Future$parallel(i, ms); | ||
return l < 1 ? Future$of([]) : new FutureClass(function Future$parallel$fork(rej, res){ | ||
let ko = false, ok = 0, cleared = false; | ||
const clears = [], out = new Array(l); | ||
const next = j => i < l ? run(ms[i], i++) : (j === l && res(out)); | ||
const run = (m, j) => { | ||
check$parallel$m(m, j); | ||
clears.push(fork(m, | ||
e => cleared || ko || (rej(e), ko = true), | ||
x => cleared || ko || (out[j] = x, next(++ok)) | ||
)); | ||
} | ||
ms.slice(0, i).forEach(run); | ||
return function Future$parallel$clear(){ | ||
cleared = true; | ||
clears.forEach(f => f()); | ||
}; | ||
let ko = false; | ||
let ok = 0; | ||
const out = new Array(l); | ||
const next = j => i < l ? fork(ms[i], i++) : (j === l && res(out)); | ||
const fork = (m, j) => (check$parallel$m(m, j), m._f( | ||
e => ko || (rej(e), ko = true), | ||
x => ko || (out[j] = x, next(++ok)) | ||
)); | ||
ms.slice(0, i).forEach(fork); | ||
}); | ||
@@ -643,0 +572,0 @@ }; |
{ | ||
"name": "fluture", | ||
"version": "0.6.0-beta2", | ||
"version": "0.6.1", | ||
"description": "A mathematically correct alternative to Promises for asynchronous control flow", | ||
@@ -15,6 +15,5 @@ "main": "fluture.js", | ||
"setup": "npm run post-merge && cp scripts/hooks/* .git/hooks && git config push.followTags true", | ||
"test": "npm run check-version && npm run clean && npm run lint && npm run test:coverage", | ||
"test": "npm run check-version && npm run clean && npm run lint && npm run test:unit", | ||
"test:opt": "node --allow-natives-syntax --trace-opt --trace-deopt --trace-inlining scripts/test-opt", | ||
"test:unit": "node --harmony-destructuring ./node_modules/.bin/_mocha --ui bdd --reporter spec --check-leaks --full-trace", | ||
"test:coverage": "node --harmony-destructuring node_modules/.bin/istanbul cover --report html ./node_modules/.bin/_mocha -- --ui bdd --reporter dot --bail --check-leaks && codecov" | ||
"test:unit": "node --harmony-destructuring node_modules/.bin/istanbul cover --report html ./node_modules/.bin/_mocha -- --ui bdd --reporter spec --check-leaks --full-trace && codecov" | ||
}, | ||
@@ -21,0 +20,0 @@ "author": "Aldwin Vlasblom <aldwin.vlasblom@gmail.com> (https://github.com/Avaq)", |
264
README.md
@@ -8,3 +8,3 @@ # Fluture | ||
[![Build Status](https://travis-ci.org/Avaq/Fluture.svg?branch=master)](https://travis-ci.org/Avaq/Fluture) | ||
[![Code Coverage](https://codecov.io/github/Avaq/Fluture/coverage.svg?branch=master)](https://codecov.io/github/Avaq/Fluture/fluture.js?branch=master) | ||
[![Code Coverage](https://codecov.io/github/Avaq/Fluture/coverage.svg?branch=develop)](https://codecov.io/github/Avaq/Fluture/fluture.js?branch=develop) | ||
@@ -19,16 +19,3 @@ Futures are containers which represent some eventual value as a result of an | ||
## Table of contents | ||
- [Usage](#usage) | ||
- [Motivation and Features](#motivation-and-features) | ||
- [Documentation](#documentation) | ||
- [Type signatures](#type-signatures) | ||
- [Creation](#creation) | ||
- [Method API](#method-api) | ||
- [Dispatcher API](#dispatcher-api) | ||
- [Cancellation and resource disposal](#cancellation-and-resource-disposal) | ||
- [Futurization](#futurization) | ||
- [Benchmarks](#benchmarks) | ||
- [The name](#the-name) | ||
## Usage | ||
@@ -64,17 +51,11 @@ | ||
## Motivation and Features | ||
## Motivation | ||
Existing implementations of Future are a pain to debug. This library was made in | ||
an effort to provide **great error messages** when something goes wrong. Some | ||
other features include: | ||
an effort to provide **great error messages** when something goes wrong. The | ||
library also comes bundled with many **async control utilities**. To prevent | ||
these features from coming at the cost of performance, Fluture was optimized to | ||
operate at **high performance**. For an overview of differences between Fluture | ||
and other Future implementations, look at [this wiki article][15]. | ||
* Plenty of async control utilities, like | ||
[`Future.parallel`](#parallel--positiveinteger---future-a-b---future-a-b) | ||
and [`Future#or`](#or--future-a-b--future-a-b---future-a-b). | ||
* [Process cancellation and automatic resource disposal](#cancellation-and-resource-disposal). | ||
* High performance. | ||
For an overview of differences between Fluture and other Future implementations, | ||
have a look at [this wiki article][15]. | ||
## Documentation | ||
@@ -98,21 +79,13 @@ | ||
### Creation | ||
### Constructors | ||
#### `Future :: ((a -> ()), (b -> ()) -> ?(() -> ())) -> ?Bool -> Future a b` | ||
#### `Future :: ((a -> Void), (b -> Void) -> Void) -> Future a b` | ||
The Future constructor. Creates a new instance of Future. Takes two parameters: | ||
A (monadic) container which represents an eventual value. A lot like Promise but | ||
more principled in that it follows the [Fantasy Land][1] algebraic JavaScript | ||
specification. | ||
* `fork`: A function which takes two callbacks. Both are continuations for an | ||
asynchronous computation. The first is `reject`, commonly abreviated to `rej`. | ||
The second `resolve`, which abreviates to `res`. The `fork` function is | ||
expected to call `rej` once an error occurs, or `res` with the result of the | ||
asynchronous computation. Additionally, another function *may* be returned to | ||
be used for [cancellation or resource disposal](#cancellation-and-resource-disposal). | ||
* `autoclear`: Whether to [automatically dispose of resources](#automatic-resource-disposal) | ||
when a resource disposal function is returned from `fork`. Defaults to true. | ||
It's quite likely you'll never need to override this. | ||
```js | ||
const eventualThing = Future((rej, res) => { | ||
setTimeout(res, 500, 'world'); | ||
const eventualThing = Future((reject, resolve) => { | ||
setTimeout(resolve, 500, 'world'); | ||
}); | ||
@@ -127,7 +100,5 @@ | ||
#### `of :: a -> Future _ a` | ||
#### `of :: b -> Future a b` | ||
Creates a Future which immediately resolves with the given value. This function | ||
is compliant with the [Fantasy Land Applicative specification][16] and is | ||
also available on the prototype. | ||
A constructor that creates a Future containing the given value. | ||
@@ -143,10 +114,5 @@ ```js | ||
#### `reject :: a -> Future a _` | ||
Creates a Future which immediately rejects with the given value. Just like `of` | ||
but for the rejection branch. | ||
#### `after :: Number -> b -> Future a b` | ||
Creates a Future which resolves with the given value after n milliseconds. | ||
A constructor that creates a Future containing the given value after n milliseconds. | ||
@@ -180,6 +146,6 @@ ```js | ||
#### `try :: (() -> !a | b) -> Future a b` | ||
#### `try :: (Void -> !a | b) -> Future a b` | ||
Creates a Future which resolves with the result of calling the given function, | ||
or rejects with the error thrown by the given function. | ||
A constructor that creates a Future which resolves with the result of calling | ||
the given function, or rejects with the error thrown by the given function. | ||
@@ -195,10 +161,9 @@ Sugar for `Future.encase(f, undefined)`. | ||
#### `node :: ((a, b -> ()) -> ()) -> Future a b` | ||
#### `node :: ((a, b -> Void) -> Void) -> Future a b` | ||
Creates a Future which rejects with the first argument given to the function, | ||
or resolves with the second if the first is not present. | ||
A constructor that creates a Future which rejects with the first argument given | ||
to the function, or resolves with the second if the first is not present. | ||
This is a convenience for NodeJS users who wish to easily obtain a Future from | ||
a node style callback API. To permanently turn a function into one that returns | ||
a Future, check out [futurization](#futurization). | ||
a node style callback API. | ||
@@ -255,11 +220,34 @@ ```js | ||
#### `cache :: Future a b -> Future a b` | ||
Returns a Future which caches the resolution value of the given Future so that | ||
whenever it's forked, it can load the value from cache rather than reexecuting | ||
the chain. | ||
```js | ||
const eventualPackage = Future.cache( | ||
Future.node(done => { | ||
console.log('Reading some big data'); | ||
fs.readFile('package.json', 'utf8', done) | ||
}) | ||
); | ||
eventualPackage.fork(console.error, console.log); | ||
//> "Reading some big data" | ||
//> "{...}" | ||
eventualPackage.fork(console.error, console.log); | ||
//> "{...}" | ||
``` | ||
### Method API | ||
#### `fork :: Future a b ~> (a -> ()), (b -> ()) -> () -> ()` | ||
#### `fork :: Future a b ~> (a -> Void), (b -> Void) -> Void` | ||
Execute the Future by calling the `fork` function that was passed to it at | ||
[construction](#creation) with the `reject` and `resolve` callbacks. Futures are | ||
*lazy*, which means even if you've `map`ped or `chain`ed over them, they'll do | ||
*nothing* if you don't eventually fork them. | ||
Execute the Future (which up until now was merely a container for its | ||
computation), and get at the result or error. | ||
It is the return from Fantasy Land to the real world. This function best shows | ||
the fundamental difference between Promises and Futures. | ||
```js | ||
@@ -281,8 +269,4 @@ Future.of('world').fork( | ||
Transforms the resolution value inside the Future, and returns a new Future with | ||
the transformed value. This is like doing `promise.then(x => x + 1)`, except | ||
that it's lazy, so the transformation will not be applied before the Future is | ||
forked. The transformation is only applied to the resolution branch. So if the | ||
Future is rejected, the transformation is ignored. To learn more about the exact | ||
behaviour of `map`, check out its [spec][12]. | ||
Map over the value inside the Future. If the Future is rejected, mapping is not | ||
performed. | ||
@@ -298,7 +282,4 @@ ```js | ||
Allows the creation of a new Future based on the resolution value. This is like | ||
doing `promise.then(x => Promise.resolve(x + 1))`, except that it's lazy, so the | ||
new Future will not be created until the other one is forked. The function is | ||
only ever applied to the resolution value, so is ignored when the Future was | ||
rejected. To learn more about the exact behaviour of `chain`, check out its [spec][13]. | ||
FlatMap over the value inside the Future. If the Future is rejected, chaining is | ||
not performed. | ||
@@ -314,4 +295,4 @@ ```js | ||
Chain over the **rejection** reason of the Future. This is like `chain`, but for | ||
the rejection branch. | ||
FlatMap over the **rejection** value inside the Future. If the Future is | ||
resolved, chaining is not performed. | ||
@@ -329,7 +310,4 @@ ```js | ||
Apply the resolution value, which is expected to be a function (as in | ||
`Future.of(a_function)`), to the resolution value in the given Future. Both | ||
Futures involved will run in parallel, and if one rejects the resulting Future | ||
will also be rejected. To learn more about the exact behaviour of `ap`, check | ||
out its [spec][14]. | ||
Apply the value in the Future to the value in the given Future. If the Future is | ||
rejected, applying is not performed. | ||
@@ -399,32 +377,4 @@ ```js | ||
#### `cache :: Future a b ~> Future a b` | ||
#### `value :: Future a b ~> (b -> Void) -> Void` | ||
Returns a cached version of the Future so that whenever it's forked, it can load | ||
the value from cache rather than reexecuting the chain. This means you can use | ||
the same Future in multiple `chain`s, without having to worry that it's going | ||
to re-execute the computation every time. | ||
Please note that cached Futures, and Futures derived from them do not | ||
automatically [dispose of resources](#automatic-resource-disposal). When the | ||
cached Future is [cancelled or disposed manually](#manual-resource-disposal-and-cancellation), | ||
it will clear its internal cache. The next time it's forked *after* that, it | ||
must re-execute the underlying Future to populate its cache again. | ||
```js | ||
const eventualPackage = Future.node(done => { | ||
console.log('Reading some big data'); | ||
fs.readFile('package.json', 'utf8', done) | ||
}) | ||
.cache(); | ||
eventualPackage.fork(console.error, console.log); | ||
//> "Reading some big data" | ||
//> "{...}" | ||
eventualPackage.fork(console.error, console.log); | ||
//> "{...}" | ||
``` | ||
#### `value :: Future a b ~> (b -> ()) -> () -> ()` | ||
Extracts the value from a resolved Future by forking it. Only use this function | ||
@@ -456,3 +406,3 @@ if you are sure the Future is going to be resolved, for example; after using | ||
#### `fork :: (a -> ()) -> (b -> ()) -> Future a b -> () -> ()` | ||
#### `fork :: (a -> Void) -> (b -> Void) -> Future a b -> Void` | ||
@@ -520,93 +470,6 @@ Dispatches the first and second arguments to the `fork` method of the third argument. | ||
#### `cache :: Future a b -> Future a b` | ||
#### `value :: (b -> Void) -> Future a b -> Void` | ||
Dispatches to the `cache` method. | ||
```js | ||
const cachedConnection = Future.cache(mysqlConnect()); | ||
``` | ||
#### `value :: (b -> ()) -> Future a b -> () -> ()` | ||
Dispatches the first argument to the `value` method of the second argument. | ||
#### `promise :: Future a b -> Promise b a` | ||
Dispatches to the `promise` method. | ||
```js | ||
Future.promise(Future.after(300, 'Hello')).then(console.log); | ||
//> "Hello" | ||
``` | ||
### Cancellation and resource disposal | ||
#### Automatic resource disposal | ||
When a Future is created, it is given the `fork` function. This `fork` function | ||
sometimes creates resources, like `setTimeout`s in the event loop or database | ||
connections. In order to deal with the disposal of these resources, one may | ||
*return* a function from `fork`, which will be automatically called after the | ||
Future has forked. This function is expected to be idempotent. | ||
It's the responsibility of this `fork` function to prevent `rej` or `res` from | ||
being called after its returned disposal function has been called. All of the | ||
functions in Fluture play by these rules. | ||
```js | ||
const createDatabaseConnection = settings => Future((rej, res) => { | ||
const conn = mysql.connect(settings, res); | ||
return () => conn.hasEnded || conn.end(); | ||
}); | ||
createDatabaseConnection() | ||
.chain(conn => conn.query('SELECT 1 + 1 AS two')) | ||
//When we `fork`, all of the resource disposers will be automatically called. | ||
.fork(console.error, console.log); | ||
``` | ||
If you don't want a particular Future to automatically dispose of its resources, | ||
you can construct it with `false` passed as its second argument: | ||
```js | ||
Future( | ||
rej => { | ||
const id = setTimeout(rej, 2000, 'timed out') | ||
return () => clearTimeout(id); | ||
}, | ||
false //<--- | ||
) | ||
``` | ||
This Future, and any Future's derived from it through `map`, `chain`, etc. will | ||
no longer automatically dispose of their resources after being forked. This | ||
allows resources to only be disposed manually. | ||
#### Manual resource disposal and cancellation | ||
Both `.fork()` and `.value()` return the disposal function and you can call it | ||
at any time to manually dispose of resources. | ||
Besides resource disposal, this function can also be used to cancel running | ||
Futures. When called prematurely it will dispose the resources and cause the | ||
whole pipeline to come to a halt: | ||
```js | ||
const loadPage = url => Future((rej, res) => { | ||
const req = new XMLHttpRequest(); | ||
req.addEventListener('load', res); | ||
req.addEventListener('error', rej); | ||
req.open('GET', url); | ||
req.send(); | ||
return () => (req.readyState < 4) && req.abort(); | ||
}); | ||
const cancel = loadPage('https://github.com').fork(onFailure, onSuccess); | ||
//Cancel the Future immediately. Nothing will happen; `onFailure` nor | ||
//`onSuccess` will ever be called anymore because `cancel` called `req.abort()`. | ||
cancel(); | ||
``` | ||
### Futurization | ||
@@ -662,2 +525,1 @@ | ||
[15]: https://github.com/Avaq/Fluture/wiki/Comparison-of-Future-Implementations | ||
[16]: https://github.com/fantasyland/fantasy-land#applicative |
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
34737
467
509