Comparing version 0.1.0 to 0.1.1
75
index.js
var nextTick = require('next-tick') | ||
, Spec = require('result-core') | ||
var Type = require('result-core/type') | ||
, nextTick = require('next-tick') | ||
, inherit = require('inherit') | ||
module.exports = Result | ||
Result.type = Spec | ||
Result.wrap = | ||
Result.done = wrap | ||
Result.type = Type | ||
Result.wrap = Result.done = wrap | ||
Result.failed = failed | ||
inherit(Result, Spec) | ||
inherit(Result, Type) | ||
@@ -19,2 +18,9 @@ function Result () { | ||
/** | ||
* default state | ||
* @type {String} | ||
*/ | ||
Result.prototype.state = 'pending' | ||
/** | ||
* Read the value of `this` | ||
@@ -75,12 +81,12 @@ * | ||
/** | ||
* break the Result | ||
* put the Result into a failed state | ||
* | ||
* @param {Any} [e] the reason for rejection | ||
* @return {Self} | ||
* @param {x} reason | ||
* @return {this} | ||
*/ | ||
Result.prototype.error = function(e){ | ||
Result.prototype.error = function(reason){ | ||
if (this.state === 'pending') { | ||
this.state = 'fail' | ||
this.value = e | ||
this.value = reason | ||
var child = this[0] | ||
@@ -90,8 +96,8 @@ var i = 1 | ||
do { | ||
if (!child._onError) child.error(e) | ||
else propagate(child, child._onError, e) | ||
if (!child._onError) child.error(reason) | ||
else propagate(child, child._onError, reason) | ||
} while (child = this[i++]) | ||
} else { | ||
Result.onError && Result.onError(this, e) | ||
return this | ||
} | ||
Result.onError && Result.onError(this, reason) | ||
} | ||
@@ -115,3 +121,3 @@ return this | ||
// auto lift one level | ||
if (value instanceof Spec) { | ||
if (value instanceof Type) { | ||
return value.read( | ||
@@ -171,3 +177,3 @@ function(val){ child.write(val) }, | ||
if (result instanceof Spec) { | ||
if (result instanceof Type) { | ||
if (result instanceof Result) return result | ||
@@ -264,2 +270,37 @@ return extract(result) | ||
return this.then(function(){ return value }) | ||
} | ||
/** | ||
* Called when a Result enters the "fail" state without | ||
* any readers to pass the `error` to | ||
* | ||
* @param {Result} result | ||
* @param {x} error | ||
*/ | ||
Result.onError = function(result, error){ | ||
result._throw = setTimeout(function(){ | ||
if (error instanceof Error) { | ||
error.message += ' (from a "failed" Result)' | ||
throw error | ||
} | ||
if (typeof console == 'object') { | ||
console.warn('%s (from a "failed" Result)', error) | ||
} | ||
}, 1000) | ||
} | ||
/** | ||
* Called when a Result in "fail" state has `read` | ||
* or `then` called with and `onError` handler i.e. | ||
* when a failed result is handled | ||
* | ||
* @param {Result} result | ||
*/ | ||
Result.onCatch = function(result){ | ||
if (result._throw !== undefined) { | ||
clearTimeout(result._throw) | ||
result._throw = undefined | ||
} | ||
} |
{ | ||
"name": "result", | ||
"version": "0.1.0", | ||
"version": "0.1.1", | ||
"description": "reify your results", | ||
@@ -5,0 +5,0 @@ "dependencies": { |
104
Readme.md
# result | ||
reify your results | ||
Reify your function's result. In JavaScript (JS) function calls result in the creation of a stack frame behind the scenes. This contains the state of the function as its code is being processed. On completion the stack frame is popped of the stack and a value is effectively substituted into the place where the function was originally called. You can say the result travels back up the stack which usually maps to traveling backwards through your source code. However functions aren't always given correct input and therefore can't always return correct values. To handle this we `throw` values instead of `return`ing them (usually that "value" is an `Error` instance). The JS engine handles `throw` in a similar way to `return`, that is, it walks the value back up the stack. However, while its walking back up its not looking for normal code its looking for code you have explicitly declared to be for the purpose of handling errors. In JS that means a kind of goofy `try catch` arrangement. When it finds this special error handling code it substitutes in the "value" as it would if we were `return`ing and then carries on as per usual. If it never manages to find any error handling code it logs the "value" to the console and kills the process. So we can say that whenever we code in JS we are coding for two paths, the success path and the error path. The JS engine passes values up and down these paths implicitly. That is we don't explicitly tell the engine where we want values to go, other than the `return`/`throw` path. The path values take is implied by the positioning of functions. Put one function to the right of another and their results will combine. Its a simple and kind of limited system but it makes a lot of sense give the interface we use to create programs is textual. | ||
## Getting Started | ||
A big problem arises when your programs input comes from outside of memory though. If your loading data from the hard-drive or across the Internet the CPU is going to end up spending so much time waiting around for something to work on that its ridiculous the expect to to just sit there and wait. We can't speed this data up but we might be able write our programs in such a way that the CPU can do other tasks while its waits for data required by another. Now we are talking about asynchronous or concurrent programming. We can't express this type of program to a JS engine simply by sticking two functions next to each other like we would normally though. It won't know that its meant to wait and think your asynchronous function simply `return`ed `undefined` or something. Though if we reify the concept of a functions result we can create our own dependency tree and recreate the value passing system normally provided implicitly by the JS engine in such a way that its tolerant of undefined time gaps between operations. The "result-core" module focuses purely on reifying the concept of a functions result while this module also tries to provide a mechanism for constructing dependency trees out of them with a method call "then". It also ended up reimplementing "result-core" which I don't like but it allowed for significant performance gains. | ||
I just realized this takes a bit more explaining than I originally thought. I've probably not done a very good job of it either so please let me know where you get lost if you do so I can fix my explanation. I promise its worth learning. Oh and speaking of promises if this sounds a lot like a promise implementation thats because it is :). haha same concept but hopefully less nonsense. | ||
## Examples | ||
### async programming | ||
the ultimate conclusion of this concept is actually right back where we started. i.e composing procedures by plonking them next to each other. [see](//github.com/jkroso/when/blob/master/examples/decorate.js) though in real usage it is pretty common to manipulate Results explicitly using `then` and `read`. | ||
## TODO: | ||
create a completely unoptimized version for learners to read. | ||
## Installation | ||
_With [component](//github.com/component/component), [packin](//github.com/jkroso/packin) or [npm](//github.com/isaacs/npm)_ | ||
@@ -15,3 +29,4 @@ | ||
```js | ||
var result = require('result') | ||
var Result = require('result') | ||
var defer = require('result/lazy') | ||
``` | ||
@@ -21,8 +36,89 @@ | ||
- [result()](#result) | ||
- [result()](#result) | ||
- [Result.read()](#resultreadonvaluefunctiononerrorfunction) | ||
- [Result.write()](#resultwritevaluex) | ||
- [Result.error()](#resulterrorreasonx) | ||
- [Result.then()](#resultthenonvaluefunctiononerrorfunction) | ||
- [Result.always()](#resultalwaysfnfunction) | ||
- [Result.node()](#resultnodecallbackerrorfunction) | ||
- [Result.yeild()](#resultyeildvaluex) | ||
- [failed()](#failed) | ||
- [wrap()](#wrap) | ||
- [defer()](#deferfunction) | ||
### result() | ||
the Result class | ||
### Result.read(onValue:Function, onError:Function) | ||
Read the value of `this` | ||
### Result.write([value]:x) | ||
Give the Result it's value | ||
### Result.error(reason:x) | ||
put the Result into a failed state | ||
### Result.then(onValue:Function, onError:Function) | ||
Create a Result for a transformation of the value | ||
of `this` Result | ||
### Result.always(fn:Function) | ||
use the same `fn` for both `onValue` and `onError` | ||
### Result.node(callback(error,:Function) | ||
read using a node style function | ||
```js | ||
result.node(function(err, value){}) | ||
``` | ||
### Result.yeild(value:x) | ||
Create a child Result destined to fulfill with `value` | ||
```js | ||
return result.then(function(value){ | ||
// something side effect | ||
}).yeild(e) | ||
``` | ||
### failed() | ||
wrap `reason` in a "failed" result | ||
### wrap() | ||
wrap `value` in a "done" Result | ||
### defer(ƒ:Function) | ||
create a DeferredResult which is associated with procedure `ƒ`. | ||
`ƒ` will only be evaluated once someone actually reads from the | ||
DeferredResult. `then` returns a normal Result so from there on | ||
out you revert to eager evaluation. For a fully fledged lazy | ||
evaluation strategy [see](//github.com/jkroso/lazy-result). | ||
```js | ||
var results = ['google.com', 'bing.com'].map(function(engine){ | ||
return defer(function(write, error){ | ||
return request(engine+'?q=hello') | ||
.on('response', write) | ||
.on('error', error) | ||
}) | ||
}) | ||
detect(results, function(result){ | ||
return result.ok | ||
}).then(display) | ||
``` | ||
## Running the tests | ||
Just run `make`. It will install and start a development server so all you then need to do is point your browser to `localhost:3000/test`. Likewise to run the examples. |
13792
4
309
123