Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

core: Introduced custom Error object

  • Loading branch information...
commit 5990361d194a39c3b4a666080bb4157704170a6b 1 parent 829b9bb
@koichik authored
View
1  CHANGELOG.md
@@ -2,6 +2,7 @@
- 0.0.2 (Not yet released)
- core: Added logging for debug.
+ - core: Introduced custom Error object.
- core: Fixed case where `functions` or `array` is empty.
- extras: Moved into `extras` namespace.
- extras: Added `flattenFirst()`, `flattenSecond()` and `flattenThird()`.
View
71 README.md
@@ -129,17 +129,17 @@ When an error is passed to a callback.
```javascript
flowless.runSeq([
- function one(cb) {
+ function foo(cb) {
cb(null);
},
- function two(cb) {
- cb(new Error('two faild')); // Error is passed
+ function bar(cb) {
+ cb(new Error('bar faild')); // Error is passed to cb
},
- function three(cb) {
+ function baz(cb) {
assert.fail('unreachable');
}
], function allDone(err, result) {
- console.log(err); // [Error: two faild]
+ console.log(err);
});
```
@@ -147,23 +147,58 @@ When an exception is thrown.
```javascript
flowless.runSeq([
- function one(cb) {
+ function foo(cb) {
cb(null);
},
- function two(cb) {
- throw new Error('two faild'); // Error is thrown
+ function bar(cb) {
+ throw new Error('bar faild'); // Error is thrown
},
- function three(cb) {
+ function baz(cb) {
assert.fail('unreachable');
}
], function allDone(err, result) {
- console.log(err); // [Error: two faild]
+ console.log(err);
});
```
In both of the cases, `three()` is not called and `Error` is passed to
the `allDone()`.
+Flowless wraps the cause error. It has the information about the location
+in the control-flow that the error occurred.
+
+Example:
+
+```javascsript
+var flowless = require('flowless');
+flowless.runSeq([
+ function foo(cb) {
+ cb(null);
+ },
+ flowless.seq([
+ function bar(cb) {
+ cb(new Error('Oooops!!'));
+ }
+ ]),
+], function(err, result) {
+ console.log(err);
+});
+```
+
+Result:
+
+ { [Error: seq[0] at (/tmp/error.js:6) failed: [Error: Oooops!!]]
+ cause: [Error: Oooops!!],
+ history:
+ [ { operator: 'seq',
+ index: 0,
+ location: '(/tmp/error.js:6)',
+ reason: 'failed' },
+ { operator: 'runSeq',
+ index: 1,
+ location: '(/tmp/error.js:2)',
+ reason: 'failed' } ] }
+
### Logging for debug
If `NODE\_DEBUG` environment variable contains `flowless`,
@@ -171,20 +206,20 @@ flowless outputs logs for debug.
For example, the first example of this page, it is output log as follows:
$ NODE_DEBUG=flowless node ex.js
- FLOWLESS: BEGIN seq at (/tmp/ex.js:4)
- FLOWLESS: begin seq[0] at (/tmp/ex.js:4) with: [ [Function: next] ]
+ FLOWLESS: BEGIN runSeq at (/tmp/ex.js:4)
+ FLOWLESS: begin runSeq[0] at (/tmp/ex.js:4) with: [ [Function: next] ]
FLOWLESS: BEGIN par at (/tmp/ex.js:5)
FLOWLESS: begin par[0] at (/tmp/ex.js:5) with: [ [Function], 'path1', 'utf8' ]
FLOWLESS: begin par[1] at (/tmp/ex.js:5) with: [ [Function], 'path2', 'utf8' ]
FLOWLESS: end par[0] at (/tmp/ex.js:5) with: [ null, 'aaa\nbbb\nccc\n' ]
FLOWLESS: end par[1] at (/tmp/ex.js:5) with: [ null, 'xxx\nyyy\nzzz\n' ]
FLOWLESS: END par at (/tmp/ex.js:5) with: [ 'aaa\nbbb\nccc\n', 'xxx\nyyy\nzzz\n' ]
- FLOWLESS: end seq[0] at (/tmp/ex.js:4) with : [ null, [ 'aaa\nbbb\nccc\n', 'xxx\nyyy\nzzz\n' ] ]
- FLOWLESS: begin seq[1] at (/tmp/ex.js:4) with: [ [ 'aaa\nbbb\nccc\n', 'xxx\nyyy\nzzz\n' ], [Function: next] ]
- FLOWLESS: end seq[1] at (/tmp/ex.js:4) with : [ null ]
- FLOWLESS: begin seq[2] at (/tmp/ex.js:4) with: [ [Function: next] ]
- FLOWLESS: end seq[2] at (/tmp/ex.js:4) with : [ null, 'aaa\nbbb\nccc\nxxx\nyyy\nzzz\n' ]
- FLOWLESS: END seq at (/tmp/ex.js:4)
+ FLOWLESS: end runSeq[0] at (/tmp/ex.js:4) with : [ null, [ 'aaa\nbbb\nccc\n', 'xxx\nyyy\nzzz\n' ] ]
+ FLOWLESS: begin runSeq[1] at (/tmp/ex.js:4) with: [ [ 'aaa\nbbb\nccc\n', 'xxx\nyyy\nzzz\n' ], [Function: next] ]
+ FLOWLESS: end runSeq[1] at (/tmp/ex.js:4) with : [ null ]
+ FLOWLESS: begin runSeq[2] at (/tmp/ex.js:4) with: [ [Function: next] ]
+ FLOWLESS: end runSeq[2] at (/tmp/ex.js:4) with : [ null, 'aaa\nbbb\nccc\nxxx\nyyy\nzzz\n' ]
+ FLOWLESS: END runSeq at (/tmp/ex.js:4)
aaa
bbb
ccc
View
12 examples/error.js
@@ -8,7 +8,11 @@ flowless.runSeq([
cb(null);
},
function two(cb) {
- cb(new Error('two failed'));
+ flowless.runSeq([
+ function inner(cb) {
+ cb(new Error('two-inner failed'));
+ }
+ ], cb);
},
function three(cb) {
assert.fail('unreachable');
@@ -22,7 +26,11 @@ flowless.runSeq([
cb(null);
},
function two(cb) {
- throw new Error('two failed');
+ flowless.runSeq([
+ function inner(cb) {
+ throw new Error('two-inner failed');
+ }
+ ], cb);
},
function three(cb) {
assert.fail('unreachable');
View
81 lib/flowless.js
@@ -8,25 +8,28 @@ exports.map = map;
exports.runMap = runMap;
var assert = require('assert');
+var util = require('util');
+
var utils = require('./utils');
var toArray = utils.toArray;
+var getLocation = utils.getLocation
var DEFAULT_CONCURRENCY = 10;
function seq(functions) {
- return setupSeq(utils.getLocation(seq), functions);
+ return setupSeq(seq, functions);
}
function runSeq(functions, cb) {
- setupSeq(utils.getLocation(runSeq), functions)(cb || noop);
+ setupSeq(runSeq, functions)(cb || noop);
}
function par(functions) {
- return setupPar(utils.getLocation(par), functions);
+ return setupPar(par, functions);
}
function runPar(functions, cb) {
- setupPar(utils.getLocation(runPar), functions)(cb || noop);
+ setupPar(runPar, functions)(cb || noop);
}
function map(concurrency, fn) {
@@ -34,7 +37,7 @@ function map(concurrency, fn) {
fn = concurrency;
concurrency = DEFAULT_CONCURRENCY;
}
- return setupMap(utils.getLocation(map), concurrency, fn);
+ return setupMap(map, concurrency, fn);
}
function runMap(concurrency, array, fn, cb) {
@@ -44,25 +47,27 @@ function runMap(concurrency, array, fn, cb) {
array = concurrency;
concurrency = DEFAULT_CONCURRENCY;
}
- setupMap(utils.getLocation(runMap), concurrency, fn)(array, cb || noop);
+ setupMap(runMap, concurrency, fn)(array, cb || noop);
}
function noop() {
}
-function setupSeq(location, functions) {
+function setupSeq(ref, functions) {
assert(Array.isArray(functions), 'functions must be an array');
- return function doSeq() {
- utils.debug('BEGIN seq at %s', location);
+ var operator = ref.name;
+ var location = getLocation(ref);
+ return function doSeq() {
+ utils.debug('BEGIN %s at %s', operator, location);
var i = 0, n = functions.length;
var args = toArray(arguments);
var cb = args.pop();
assert(typeof cb === 'function', 'last argument must be a function');
if (n === 0) {
- utils.debug('seq %s SUCCEEDED', location);
+ utils.debug('%s %s END', operator, location);
deferCall(false, cb, [null]);
return;
}
@@ -74,25 +79,29 @@ function setupSeq(location, functions) {
function doEachSeq() {
var fn = utils.makeFunc(functions[i]);
var params = toArray(arguments).concat(next);
- utils.debug('begin seq[%d] at %s with:', i, location, params);
+ utils.debug('begin %s[%d] at %s with:', operator, i, location, params);
try {
fn.apply(null, params);
} catch (err) {
- utils.debug('failed seq[%d] at %s with:', i, location, err);
+ utils.debug('failed %s[%d] at %s thrown:', operator, i, location,
+ util.isError(err) ? err.message : err);
+ err = makeError('seq', i, location, 'thrown', err);
deferCall(returned, cb, [err]);
return;
}
function next(err) {
if (err) {
- utils.debug('failed seq[%d] at %s with :', i, location, err);
+ utils.debug('failed %s[%d] at %s with:', operator, i, location,
+ util.isError(err) ? err.message : err);
+ err = makeError(operator, i, location, 'failed', err);
deferCall(returned, cb, [err]);
return;
}
var args = toArray(arguments);
- utils.debug('end seq[%d] at %s with :', i, location, args);
+ utils.debug('end %s[%d] at %s with :', operator, i, location, args);
if (++i >= n) {
- utils.debug('END seq at %s', location);
+ utils.debug('END %s at %s', operator, location);
deferCall(returned, cb, args);
return;
}
@@ -102,15 +111,18 @@ function setupSeq(location, functions) {
};
}
-function setupPar(location, functions) {
+function setupPar(ref, functions) {
assert(Array.isArray(functions), 'functions must be an array');
+ var operator = ref.name;
+ var location = getLocation(ref);
+
return function doPar() {
var args = toArray(arguments);
var cb = args.pop();
assert(typeof cb === 'function', 'last argument must be a function');
- invokeParFuncs('par', location, functions.length, functions,
+ invokeParFuncs(operator, location, functions.length, functions,
function invokeFunc(fn, next) {
fn = utils.makeFunc(fn);
fn.apply(null, args.slice().concat(next));
@@ -118,14 +130,17 @@ function setupPar(location, functions) {
};
}
-function setupMap(location, concurrency, fn) {
+function setupMap(ref, concurrency, fn) {
fn = utils.makeFunc(fn);
assert(typeof fn === 'function', 'last argument must be a function');
+ var operator = ref.name;
+ var location = getLocation(ref);
+
return function doMap(array, cb) {
assert(Array.isArray(array), 'first argument must be an array');
assert(typeof cb === 'function', 'last argument must be a function');
- invokeParFuncs('map', location, concurrency, array, fn, cb);
+ invokeParFuncs(operator, location, concurrency, array, fn, cb);
};
}
@@ -158,8 +173,9 @@ function invokeParFuncs(operator, location, concurrency, array,
try {
invokeFunction(array[index], next);
} catch (err) {
- utils.debug('failed %s[%d] at %s with:',
- operator, index, location, err);
+ utils.debug('failed %s[%d] at %s thrown:', operator, index, location,
+ util.isError(err) ? err.message : err);
+ err = makeError(operator, index, location, 'thrown', err);
return reportError(returnedInner, err);
}
returnedInner = true;
@@ -169,8 +185,9 @@ function invokeParFuncs(operator, location, concurrency, array,
if (done) {
return;
} else if (err) {
- utils.debug('failed %s[%d] at %s with:',
- operator, index, location, err);
+ utils.debug('failed %s[%d] at %s with:', operator, index, location,
+ util.isError(err) ? err.message : err);
+ err = makeError(operator, index, location, 'failed', err);
return reportError(returnedInner, err);
}
@@ -211,3 +228,21 @@ function deferCall(returned, cb, args) {
});
}
}
+
+function makeError(operator, index, location, reason, cause) {
+ var err = cause;
+ if (!util.isError(cause) || !Array.isArray(err.history)) {
+ var message = util.format('%s[%d] at %s %s:',
+ operator, index, location, reason, cause);
+ err = new Error(message)
+ err.cause = cause;
+ err.history = [];
+ }
+ err.history.push({
+ operator: operator,
+ index: index,
+ location: location,
+ reason: reason
+ });
+ return err;
+}
View
30 lib/utils.js
@@ -62,25 +62,7 @@ function convertPlaceholder(args, argument) {
return args;
}
-function debug() {
- var args = Array.prototype.slice.call(arguments);
- if (typeof args[0] === 'string') {
- args[0] = 'FLOWLESS: ' + args[0];
- } else {
- args.unshift('FLOWLESS:');
- }
- console.error.apply(console, args);
-}
-
-function getLocation(referencePoint, force) {
- if (typeof referencePoint === 'boolean') {
- force = referencePoint;
- referencePoint = null;
- }
- if (!debugMode && !force) {
- return '';
- }
-
+function getLocation(referencePoint) {
var original = Error.prepareStackTrace;
Error.prepareStackTrace = customPrepareStackTrace;
var error = {};
@@ -96,3 +78,13 @@ function getLocation(referencePoint, force) {
return structuredStackTrace[0];
}
}
+
+function debug() {
+ var args = Array.prototype.slice.call(arguments);
+ if (typeof args[0] === 'string') {
+ args[0] = 'FLOWLESS: ' + args[0];
+ } else {
+ args.unshift('FLOWLESS:');
+ }
+ console.error.apply(console, args);
+}
View
4 test/par.js
@@ -53,7 +53,7 @@ describe('par', function() {
}
], function(err) {
should.exist(err);
- err.message.should.equal('oops');
+ err.message.should.match(/\[Error: oops]/);
done();
});
});
@@ -73,7 +73,7 @@ describe('par', function() {
}
], function(err) {
should.exist(err);
- err.message.should.equal('oops');
+ err.message.should.match(/\[Error: oops]/);
done();
});
});
View
4 test/seq.js
@@ -41,7 +41,7 @@ describe('seq', function() {
}
], function(err) {
should.exist(err);
- err.message.should.equal('oops');
+ err.message.should.match(/\[Error: oops]/);
done();
});
});
@@ -60,7 +60,7 @@ describe('seq', function() {
}
], function(err) {
should.exist(err);
- err.message.should.equal('oops');
+ err.message.should.match(/\[Error: oops]/);
done();
});
});
View
15 test/utils.js
@@ -6,20 +6,17 @@ var utils = require('../lib/utils');
describe('utils', function() {
it('should resolve location', function(done) {
- // annonymous function
- var location = utils.getLocation(true);
- location.should.match(/^\(.*/);
-
- // named function
- (function test() {
- location = utils.getLocation(true);
+ // function
+ function test() {
+ var location = utils.getLocation();
location.should.match(/^test\(.*/);
- })();
+ }
+ test();
// method
function Foo() {};
Foo.prototype.bar = function() {
- location = utils.getLocation(true);
+ var location = utils.getLocation();
location.should.match(/^Foo\.bar\(.*/);
};
var foo = new Foo();
Please sign in to comment.
Something went wrong with that request. Please try again.