🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

aflow

Package Overview
Dependencies
Maintainers
1
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

aflow - npm Package Compare versions

Comparing version
0.10.0
to
0.10.1
+220
aflow.js
/**
* Flow control library to simplify sequencing calls with callbacks.
*
* Similar to async, but faster and more robust. Notable differences:
* - no more "call stack exceeded" from callbacks
* - callbacks must be called
* - callbacks can use process.nextTick without recursion
* - loop overhead is less
* - waterfall and series are combined
*
* Copyright (C) 2014 Andras Radics
* Licensed under the Apache License, Version 2.0
*
* 2014-09-28 - AR.
*/
module.exports.repeatUntil = repeatUntil;
module.exports.applyVisitor = applyVisitor;
module.exports.repeatWhile = repeatWhile;
module.exports.map = map;
module.exports.filter = filter;
module.exports.reduce = reduce;
module.exports.iterate = module.exports.sequence = iterate;
// aliases
module.exports.qflow = iterate;
module.exports.flow = iterate;
module.exports.select = filter;
module.exports.series = iterate;
module.exports.whilst = repeatWhile;
var qflowSetImmediate = global.setImmediate ? function(f) { setImmediate(f) } : process.nextTick
/**
* Repeatedly call func until it signals stop (returns truthy) or err.
* Func is passed just a standard callback taking err and ret.
* Returns to the callback the error or the truthy value from func.
*/
function repeatUntil( func, callback ) {
'use strict';
callback = callback || function() {};
var alreadyReturned;
var callDepth = 0;
function _invokeCallback( err, stop ) {
if (err || stop) {
if (alreadyReturned) throw new Error("callback already called");
alreadyReturned = true;
return callback(err, stop);
}
else if (++callDepth < 40) {
_loop();
}
else {
callDepth = 0;
qflowSetImmediate(_loop);
}
}
function _loop( ) {
try {
func( _invokeCallback );
}
catch (err) {
// do not inject errors from within the callback back into the callback.
// If func returns without yielding, the callback will still be in
// our try/catch block. Re-throw that error like setImmediate would do.
// But if func has not returned yet, the error is from func, so return it.
if (alreadyReturned) throw err; else _invokeCallback(err);
}
}
_loop();
}
/**
* Repeatedly call func as long as test returns truthy.
*/
function repeatWhile( test, func, callback ) {
repeatUntil(
function _loop(cb) {
if (test()) func(function(err) { cb(err, false); });
else cb(null, true);
},
callback
);
}
/**
* visitor pattern: present all items to the visitor function.
* Returns just error status, no data; to capture the results,
* wrapper the visitor function.
*/
function applyVisitor( dataItems, visitorFunction, callback ) {
'use strict';
if (!dataItems || typeof dataItems.length !== 'number') {
return callback(new Error("expected a data array, but got " + typeof data));
}
var next = 0;
repeatUntil(
function applyFunc(cb) {
if (next >= dataItems.length) return cb(null, true);
visitorFunction(dataItems[next++], function(err) {
cb(err, err);
});
},
function(err, stop) {
callback(err);
}
);
}
/**
* Run the list of functions, calling each one in turn.
* Each function is passed a callback and the data item
* returned by the previous function. Errors interrupt the flow.
* The final result is the last result generated by the sequence
* (and not all the individual results).
*/
function iterate( functionList, callback ) {
'use strict';
var lastResult, lastResult2;
var callReturned;
applyVisitor(
functionList,
function visitor(func, cb) {
// send this computed result right back to the visitor loop,
// it will become the next func`s lastResult
// forwards the first 2 results to the next function in the chain
callReturned = false;
func(
function(err, ret, ret2) {
callReturned = true;
lastResult = ret;
lastResult2 = ret2;
cb(err, ret, ret2);
},
lastResult,
lastResult2
);
},
function(err, ret) {
// return the very last result computed, that applyVisitor returns
return callback ? (callReturned ? callback(err, lastResult, lastResult2) : callback(err)) : null;
}
);
}
/**
* Combine all items in the data array using func.
* runningTotal is the initial combined value; a call to combinerFunc is given
* the running total and item and the return value is the new runningTotal.
* This is an async variant of Array.prototype.reduce().
*/
function reduce( data, runningTotal, combinerFunc, callback ) {
'use strict';
applyVisitor(
data,
function visitor(item, cb) {
combinerFunc(runningTotal, item, function(err, ret) {
// the running total is the combined results of item and the
// previous subtotal, as computed by func().
if (!err || ret !== undefined) runningTotal = ret;
return cb(err, runningTotal);
});
},
function(err, ret) {
callback(err, runningTotal);
}
);
}
/**
* Apply the function func to all the items in data, and return the
* results produced. Func must take a single argument and a callback.
* In case of error returns the partial results so far;
* the very last returned result is the one that errored out.
*/
function map( data, mapperFunc, callback ) {
// TODO: might also want to map properties of an object, not just arrays
// Approach: convert object to list, map list, de-convert results.
var mappedResults = [];
applyVisitor(
data,
function visitor(item, cb) {
mapperFunc(item, function(err, mappedItem) {
if (!err || mappedItem !== undefined) mappedResults.push(mappedItem);
cb(err);
});
},
function(err, ret) {
return callback(err, mappedResults);;
}
);
}
/**
* Return the list of items that were picked by the filter function.
* The filter function should return truthy for values to include,
* and falsy for values to omit from the results.
*/
function filter( data, filterFunc, callback ) {
var pickedItems = [];
applyVisitor(
data,
function visitor(item, cb) {
filterFunc(item, function(err, yesno) {
if (yesno) pickedItems.push(item);
cb(err);
});
},
function(err) {
callback(err, pickedItems);
}
);
}
'use strict';
var qflow = require('./index');
function uniqid() {
return Math.floor(Math.random() * 0x1000000).toString(16);
}
module.exports = {
'should include file': function(t) {
t.ok(qflow.repeatUntil);
t.done();
},
'should parse package.json': function(t) {
require('./package.json');
t.done();
},
'repeatUntil': {
'should trap errors and return them in callback': function(t) {
t.expect(2);
qflow.repeatUntil(
function(cb) {
throw new Error("die");
},
function(err, last) {
t.ok(err instanceof Error);
t.equal(err.message, "die");
t.done();
}
);
},
'should stop looping when visitor returns truthy': function(t) {
var nloops = 0;
var id = uniqid();
t.expect(2);
qflow.repeatUntil(
function(cb) {
cb(false, ++nloops >= 10 ? id : false);
},
function(err, last) {
t.equal(last, id, "expected to see value that stopped loop");
t.equal(nloops, 10, "expected 10 loopp iterations");
t.done();
}
);
},
'should not overflow stack': function(t) {
t.expect(1);
var nloops = 0;
qflow.repeatUntil(
function(cb) {
cb(false, ++nloops > 20000);
},
function(err, last) {
t.ok(nloops == 20001);
t.done();
}
);
},
'should re-throw subsequent func errors': function(t) {
t.expect(2);
try {
qflow.repeatUntil(
function(cb) {
cb(new Error("first error"));
throw new Error("second error");
},
function(err) {
t.equal(err.message, "first error");
}
);
}
catch (err) {
t.equal(err.message, "second error");
t.done();
}
},
'should re-throw callback errors': function(t) {
t.expect(1);
try {
qflow.repeatUntil(
function(cb) {
cb(null, true);
},
function(err) {
throw new Error("callback error");
}
);
}
catch (err) {
t.equal(err.message, "callback error");
t.done();
}
},
},
'repeatWhile': {
'should test loop condition at top': function(t) {
var ncalls = 0;
t.expect(1);
qflow.repeatWhile(
function() { return false; },
function(next) { ncalls += 1; next(); },
function(err) {
t.equal(ncalls, 0);
t.done();
}
);
},
'should loop n times': function(t) {
var ncalls = 0;
t.expect(1);
qflow.repeatWhile(
function() { return ncalls < 1234; },
function(next) { ncalls += 1; next(); },
function(err) {
t.equal(ncalls, 1234);
t.done();
}
);
},
'should stop loop on error': function(t) {
var ncalls = 0;
t.expect(3);
qflow.repeatWhile(
function() { return ncalls < 1234; },
function(next) { ncalls += 1; if (ncalls < 111) next(); else throw new Error("stop at 111"); },
function(err) {
t.equal(ncalls, 111);
t.ok(err instanceof Error);
t.equal(err.message, "stop at 111");
t.done();
}
);
},
},
'applyVisitor': {
'should call visitor with each item': function(t) {
var visited = [];
qflow.applyVisitor(
[1,2,3,4],
function(item, cb) {
visited.push(item);
cb();
},
function(err) {
t.deepEqual(visited, [1,2,3,4]);
t.done();
}
);
},
'should return error and stop looping on error': function(t) {
var visited = [];
t.expect(3);
qflow.applyVisitor(
[1,2,3,4],
function(item, cb) {
visited.push(item);
if (item == 3) throw new Error("stop at 3");
cb();
},
function(err) {
t.deepEqual(visited, [1,2,3]);
t.ok(err instanceof Error);
t.equal(err.message, "stop at 3");
t.done();
}
);
},
},
'iterate': {
'should call each function': function(t) {
var items = [];
t.expect(1);
qflow.iterate([
function(cb) { items.push(1); cb() },
function(cb) { items.push(2); cb() },
function(cb) { items.push(3); cb() },
],
function(err) {
t.deepEqual(items, [1,2,3]);
t.done();
}
);
},
'should pass previous result to each next function and return the last result': function(t) {
var args = [];
t.expect(3);
qflow.iterate([
function(cb, arg, arg2) { args.push(arg); args.push(arg2); cb(null, 1, 11); },
function(cb, arg, arg2) { args.push(arg); args.push(arg2); cb(null, 2, 22); },
function(cb, arg, arg2) { args.push(arg); args.push(arg2); cb(null, 3, 33); },
],
function(err, arg, arg2) {
t.deepEqual(args, [undefined, undefined, 1, 11, 2, 22]);
t.equal(arg, 3);
t.equal(arg2, 33);
t.done();
}
);
},
'should stop on error': function(t) {
var items = [];
t.expect(3);
qflow.iterate([
function(cb) { items.push(1); cb(); },
function(cb) { items.push(2); throw new Error("stop at 2"); cb() },
function(cb) { items.push(3); cb() },
],
function(err) {
t.deepEqual(items, [1,2]);
t.ok(err instanceof Error);
t.equal(err.message, "stop at 2");
t.done();
}
);
},
},
'reduce': {
'should combine items': function(t) {
var items = [1,2,3,4,5];
t.expect(1);
qflow.reduce(
items,
'',
function(total, item, cb) {
total += '.' + item;
cb(null, total);
},
function(err, final) {
t.equal(final, '.1.2.3.4.5');
t.done();
}
);
},
'returns errors along with partial results': function(t) {
var items = [1,2,3,4,5];
t.expect(3);
qflow.reduce(
items,
'',
function(total, item, cb) {
if (item === 3) throw new Error("err at 3");
total += '.' + item;
cb(null, total);
},
function(err, final) {
t.ok(err instanceof Error);
t.equal(err.message, "err at 3");
t.equal(final, ".1.2");
t.done();
}
);
},
},
'map': {
'should apply function to all items': function(t) {
var items = [1,2,3,4];
t.expect(1);
qflow.map(
items,
function(item, cb) { cb(null, item + item); },
function(err, mapped) {
t.deepEqual(mapped, [2,4,6,8]);
t.done();
}
);
},
'should return error and partial results': function(t) {
var items = [1,2,3,4];
t.expect(3);
qflow.map(
items,
function(item, cb) { if (item === 3) throw new Error("three"); else cb(null, item + item); },
function(err, mapped) {
t.deepEqual(mapped, [2,4]);
t.ok(err instanceof Error);
t.equal(err.message, "three");
t.done();
}
);
},
'should include a defined item that came with error': function(t) {
var items = [1,2,3,4];
t.expect(1);
qflow.map(
items,
function(item, cb) { cb((item === 3 ? new Error("three") : null), item); },
function(err, mapped) {
t.equal(mapped[2], 3);
t.done();
}
);
},
},
'filter': {
'should return array of items selected by filter function': function(t) {
var items = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];
t.expect(1);
qflow.filter(
items,
function(item, cb) {
// odd multiples of 3
var yesInclude = (item % 2) && (item % 3 == 0)
cb(null, yesInclude);
},
function(err, found) {
t.deepEqual(found, [3, 9, 15]);
t.done();
}
);
},
'should return any errors': function(t) {
var items = [1,2,3,4];
t.expect(3);
qflow.filter(
items,
function(item, cb) { if (item == 3) throw new Error("err on 3"); cb(null, true); },
function(err, found) {
t.ok(err instanceof Error);
t.equal(err.message, "err on 3");
t.deepEqual(found, [1,2]);
t.done();
}
);
},
'should include a defined item that came with error': function(t) {
var items = [1,2,3,4];
t.expect(1);
qflow.filter(
items,
function(item, cb) { cb((item === 3 ? new Error("three") : null), item); },
function(err, picked) {
t.equal(picked[2], 3);
t.done();
}
);
},
},
};
'use strict';
var qflow = require('./index');
module.exports = {
'repeatUntil 500k': function(t) {
var n = 0;
t.expect(2);
qflow.repeatUntil(
function(cb) {
n++;
cb(null, n >= 500000);
},
function(err, flag) {
t.equal(flag, true);
t.equal(n, 500000);
t.done();
}
);
},
};
+1
-1

@@ -1,1 +0,1 @@

module.exports = require('./lib/qflow');
module.exports = require('./aflow');
{
"name": "aflow",
"version": "0.10.0",
"version": "0.10.1",
"description": "async flow control for calls with callbacks",

@@ -19,3 +19,3 @@ "license": "Apache-2.0",

"scripts": {
"test": "nodeunit"
"test": "qnit test-*"
},

@@ -41,4 +41,4 @@ "keywords": [

"devDependencies": {
"nodeunit": "0.9.0"
"qnit": "0.11.0"
}
}

@@ -167,2 +167,7 @@ aflow

0.10.1
- global.setImmediate(f) is more reliable as function(){ setImmediate(f) }
- flatten source tree
0.10.0

@@ -169,0 +174,0 @@

/**
* Flow control library to simplify sequencing calls with callbacks.
*
* Similar to async, but faster and more robust. Notable differences:
* - no more "call stack exceeded" from callbacks
* - callbacks must be called
* - callbacks can use process.nextTick without recursion
* - loop overhead is less
* - waterfall and series are combined
*
* Copyright (C) 2014 Andras Radics
* Licensed under the Apache License, Version 2.0
*
* 2014-09-28 - AR.
*/
module.exports.repeatUntil = repeatUntil;
module.exports.applyVisitor = applyVisitor;
module.exports.repeatWhile = repeatWhile;
module.exports.map = map;
module.exports.filter = filter;
module.exports.reduce = reduce;
module.exports.iterate = module.exports.sequence = iterate;
// aliases
module.exports.qflow = iterate;
module.exports.flow = iterate;
module.exports.select = filter;
module.exports.series = iterate;
module.exports.whilst = repeatWhile;
var qflowSetImmediate = global.setImmediate || process.nextTick
/**
* Repeatedly call func until it signals stop (returns truthy) or err.
* Func is passed just a standard callback taking err and ret.
* Returns to the callback the error or the truthy value from func.
*/
function repeatUntil( func, callback ) {
'use strict';
callback = callback || function() {};
var alreadyReturned;
var callDepth = 0;
function _invokeCallback( err, stop ) {
if (err || stop) {
if (alreadyReturned) throw new Error("callback already called");
alreadyReturned = true;
return callback(err, stop);
}
else if (++callDepth < 40) {
_loop();
}
else {
callDepth = 0;
qflowSetImmediate(_loop);
}
}
function _loop( ) {
try {
func( _invokeCallback );
}
catch (err) {
// do not inject errors from within the callback back into the callback.
// If func returns without yielding, the callback will still be in
// our try/catch block. Re-throw that error like setImmediate would do.
// But if func has not returned yet, the error is from func, so return it.
if (alreadyReturned) throw err; else _invokeCallback(err);
}
}
_loop();
}
/**
* Repeatedly call func as long as test returns truthy.
*/
function repeatWhile( test, func, callback ) {
repeatUntil(
function _loop(cb) {
if (test()) func(function(err) { cb(err, false); });
else cb(null, true);
},
callback
);
}
/**
* visitor pattern: present all items to the visitor function.
* Returns just error status, no data; to capture the results,
* wrapper the visitor function.
*/
function applyVisitor( dataItems, visitorFunction, callback ) {
'use strict';
if (!dataItems || typeof dataItems.length !== 'number') {
return callback(new Error("expected a data array, but got " + typeof data));
}
var next = 0;
repeatUntil(
function applyFunc(cb) {
if (next >= dataItems.length) return cb(null, true);
visitorFunction(dataItems[next++], function(err) {
cb(err, err);
});
},
function(err, stop) {
callback(err);
}
);
}
/**
* Run the list of functions, calling each one in turn.
* Each function is passed a callback and the data item
* returned by the previous function. Errors interrupt the flow.
* The final result is the last result generated by the sequence
* (and not all the individual results).
*/
function iterate( functionList, callback ) {
'use strict';
var lastResult, lastResult2;
var callReturned;
applyVisitor(
functionList,
function visitor(func, cb) {
// send this computed result right back to the visitor loop,
// it will become the next func`s lastResult
// forwards the first 2 results to the next function in the chain
callReturned = false;
func(
function(err, ret, ret2) {
callReturned = true;
lastResult = ret;
lastResult2 = ret2;
cb(err, ret, ret2);
},
lastResult,
lastResult2
);
},
function(err, ret) {
// return the very last result computed, that applyVisitor returns
return callback ? (callReturned ? callback(err, lastResult, lastResult2) : callback(err)) : null;
}
);
}
/**
* Combine all items in the data array using func.
* runningTotal is the initial combined value; a call to combinerFunc is given
* the running total and item and the return value is the new runningTotal.
* This is an async variant of Array.prototype.reduce().
*/
function reduce( data, runningTotal, combinerFunc, callback ) {
'use strict';
applyVisitor(
data,
function visitor(item, cb) {
combinerFunc(runningTotal, item, function(err, ret) {
// the running total is the combined results of item and the
// previous subtotal, as computed by func().
if (!err || ret !== undefined) runningTotal = ret;
return cb(err, runningTotal);
});
},
function(err, ret) {
callback(err, runningTotal);
}
);
}
/**
* Apply the function func to all the items in data, and return the
* results produced. Func must take a single argument and a callback.
* In case of error returns the partial results so far;
* the very last returned result is the one that errored out.
*/
function map( data, mapperFunc, callback ) {
// TODO: might also want to map properties of an object, not just arrays
// Approach: convert object to list, map list, de-convert results.
var mappedResults = [];
applyVisitor(
data,
function visitor(item, cb) {
mapperFunc(item, function(err, mappedItem) {
if (!err || mappedItem !== undefined) mappedResults.push(mappedItem);
cb(err);
});
},
function(err, ret) {
return callback(err, mappedResults);;
}
);
}
/**
* Return the list of items that were picked by the filter function.
* The filter function should return truthy for values to include,
* and falsy for values to omit from the results.
*/
function filter( data, filterFunc, callback ) {
var pickedItems = [];
applyVisitor(
data,
function visitor(item, cb) {
filterFunc(item, function(err, yesno) {
if (yesno) pickedItems.push(item);
cb(err);
});
},
function(err) {
callback(err, pickedItems);
}
);
}
'use strict';
var qflow = require('../index');
function uniqid() {
return Math.floor(Math.random() * 0x1000000).toString(16);
}
module.exports = {
'should include file': function(t) {
t.ok(qflow.repeatUntil);
t.done();
},
'should parse package.json': function(t) {
require('../package.json');
t.done();
},
'repeatUntil': {
'should trap errors and return them in callback': function(t) {
t.expect(2);
qflow.repeatUntil(
function(cb) {
throw new Error("die");
},
function(err, last) {
t.ok(err instanceof Error);
t.equal(err.message, "die");
t.done();
}
);
},
'should stop looping when visitor returns truthy': function(t) {
var nloops = 0;
var id = uniqid();
t.expect(2);
qflow.repeatUntil(
function(cb) {
cb(false, ++nloops >= 10 ? id : false);
},
function(err, last) {
t.equal(last, id, "expected to see value that stopped loop");
t.equal(nloops, 10, "expected 10 loopp iterations");
t.done();
}
);
},
'should not overflow stack': function(t) {
t.expect(1);
var nloops = 0;
qflow.repeatUntil(
function(cb) {
cb(false, ++nloops > 20000);
},
function(err, last) {
t.ok(nloops == 20001);
t.done();
}
);
},
'should re-throw subsequent func errors': function(t) {
t.expect(2);
try {
qflow.repeatUntil(
function(cb) {
cb(new Error("first error"));
throw new Error("second error");
},
function(err) {
t.equal(err.message, "first error");
}
);
}
catch (err) {
t.equal(err.message, "second error");
t.done();
}
},
'should re-throw callback errors': function(t) {
t.expect(1);
try {
qflow.repeatUntil(
function(cb) {
cb(null, true);
},
function(err) {
throw new Error("callback error");
}
);
}
catch (err) {
t.equal(err.message, "callback error");
t.done();
}
},
},
'repeatWhile': {
'should test loop condition at top': function(t) {
var ncalls = 0;
t.expect(1);
qflow.repeatWhile(
function() { return false; },
function(next) { ncalls += 1; next(); },
function(err) {
t.equal(ncalls, 0);
t.done();
}
);
},
'should loop n times': function(t) {
var ncalls = 0;
t.expect(1);
qflow.repeatWhile(
function() { return ncalls < 1234; },
function(next) { ncalls += 1; next(); },
function(err) {
t.equal(ncalls, 1234);
t.done();
}
);
},
'should stop loop on error': function(t) {
var ncalls = 0;
t.expect(3);
qflow.repeatWhile(
function() { return ncalls < 1234; },
function(next) { ncalls += 1; if (ncalls < 111) next(); else throw new Error("stop at 111"); },
function(err) {
t.equal(ncalls, 111);
t.ok(err instanceof Error);
t.equal(err.message, "stop at 111");
t.done();
}
);
},
},
'applyVisitor': {
'should call visitor with each item': function(t) {
var visited = [];
qflow.applyVisitor(
[1,2,3,4],
function(item, cb) {
visited.push(item);
cb();
},
function(err) {
t.deepEqual(visited, [1,2,3,4]);
t.done();
}
);
},
'should return error and stop looping on error': function(t) {
var visited = [];
t.expect(3);
qflow.applyVisitor(
[1,2,3,4],
function(item, cb) {
visited.push(item);
if (item == 3) throw new Error("stop at 3");
cb();
},
function(err) {
t.deepEqual(visited, [1,2,3]);
t.ok(err instanceof Error);
t.equal(err.message, "stop at 3");
t.done();
}
);
},
},
'iterate': {
'should call each function': function(t) {
var items = [];
t.expect(1);
qflow.iterate([
function(cb) { items.push(1); cb() },
function(cb) { items.push(2); cb() },
function(cb) { items.push(3); cb() },
],
function(err) {
t.deepEqual(items, [1,2,3]);
t.done();
}
);
},
'should pass previous result to each next function and return the last result': function(t) {
var args = [];
t.expect(3);
qflow.iterate([
function(cb, arg, arg2) { args.push(arg); args.push(arg2); cb(null, 1, 11); },
function(cb, arg, arg2) { args.push(arg); args.push(arg2); cb(null, 2, 22); },
function(cb, arg, arg2) { args.push(arg); args.push(arg2); cb(null, 3, 33); },
],
function(err, arg, arg2) {
t.deepEqual(args, [undefined, undefined, 1, 11, 2, 22]);
t.equal(arg, 3);
t.equal(arg2, 33);
t.done();
}
);
},
'should stop on error': function(t) {
var items = [];
t.expect(3);
qflow.iterate([
function(cb) { items.push(1); cb(); },
function(cb) { items.push(2); throw new Error("stop at 2"); cb() },
function(cb) { items.push(3); cb() },
],
function(err) {
t.deepEqual(items, [1,2]);
t.ok(err instanceof Error);
t.equal(err.message, "stop at 2");
t.done();
}
);
},
},
'reduce': {
'should combine items': function(t) {
var items = [1,2,3,4,5];
t.expect(1);
qflow.reduce(
items,
'',
function(total, item, cb) {
total += '.' + item;
cb(null, total);
},
function(err, final) {
t.equal(final, '.1.2.3.4.5');
t.done();
}
);
},
'returns errors along with partial results': function(t) {
var items = [1,2,3,4,5];
t.expect(3);
qflow.reduce(
items,
'',
function(total, item, cb) {
if (item === 3) throw new Error("err at 3");
total += '.' + item;
cb(null, total);
},
function(err, final) {
t.ok(err instanceof Error);
t.equal(err.message, "err at 3");
t.equal(final, ".1.2");
t.done();
}
);
},
},
'map': {
'should apply function to all items': function(t) {
var items = [1,2,3,4];
t.expect(1);
qflow.map(
items,
function(item, cb) { cb(null, item + item); },
function(err, mapped) {
t.deepEqual(mapped, [2,4,6,8]);
t.done();
}
);
},
'should return error and partial results': function(t) {
var items = [1,2,3,4];
t.expect(3);
qflow.map(
items,
function(item, cb) { if (item === 3) throw new Error("three"); else cb(null, item + item); },
function(err, mapped) {
t.deepEqual(mapped, [2,4]);
t.ok(err instanceof Error);
t.equal(err.message, "three");
t.done();
}
);
},
'should include a defined item that came with error': function(t) {
var items = [1,2,3,4];
t.expect(1);
qflow.map(
items,
function(item, cb) { cb((item === 3 ? new Error("three") : null), item); },
function(err, mapped) {
t.equal(mapped[2], 3);
t.done();
}
);
},
},
'filter': {
'should return array of items selected by filter function': function(t) {
var items = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];
t.expect(1);
qflow.filter(
items,
function(item, cb) {
// odd multiples of 3
var yesInclude = (item % 2) && (item % 3 == 0)
cb(null, yesInclude);
},
function(err, found) {
t.deepEqual(found, [3, 9, 15]);
t.done();
}
);
},
'should return any errors': function(t) {
var items = [1,2,3,4];
t.expect(3);
qflow.filter(
items,
function(item, cb) { if (item == 3) throw new Error("err on 3"); cb(null, true); },
function(err, found) {
t.ok(err instanceof Error);
t.equal(err.message, "err on 3");
t.deepEqual(found, [1,2]);
t.done();
}
);
},
'should include a defined item that came with error': function(t) {
var items = [1,2,3,4];
t.expect(1);
qflow.filter(
items,
function(item, cb) { cb((item === 3 ? new Error("three") : null), item); },
function(err, picked) {
t.equal(picked[2], 3);
t.done();
}
);
},
},
};
'use strict';
var qflow = require('../index');
module.exports = {
'repeatUntil 500k': function(t) {
var n = 0;
t.expect(2);
qflow.repeatUntil(
function(cb) {
n++;
cb(null, n >= 500000);
},
function(err, flag) {
t.equal(flag, true);
t.equal(n, 500000);
t.done();
}
);
},
};