savoy v0.3.1
Savoy.js

Higher-order functions (synchronous and asynchronous each/eachSeries/map/filter/fold) and functions for flow control (parallel/series/waterfall) in under 1 KB.
- API is similar (not identical) to that of the Async library
- 2.5 KB minified, or about 0.9 KB minified and gzipped
- Legible tests, with 100% coverage
Why
Savoy’s higher-order functions differ from Async’s in the following ways:
- The signature of the
fniterator is different. Thecbcallback (invoked to signal the end of each iteration offn) is the first argument offn. In addition,fnis also passed the index/key of the current element, and the originalcollectionitself. - If passed is an Object,
mapandfilterwill return an Object. (If passed an Object, Async’smapandfilterwill return an Array.)
Also, mainly this:
Inventing your own wheels gives you a deep appreciation and understanding of how wheels work and what makes a good one.
API
In all of the method signatures below, collection (in each/eachSeries/map/filter/fold) and fns (in parallel/series/waterfall) can either be an Array or an Object literal.
each
savoy.each(collection, fn) — synchronous each
savoy.each([1, 2, 3], function(val, i, arr) {
console.log(val, i, arr);
//=> 1, 0, [ 1, 2, 3 ]
//=> 2, 1, [ 1, 2, 3 ]
//=> 3, 2, [ 1, 2, 3 ]
});Break from the loop by explicitly returning
falsefromfn:savoy.each([1, 2, 3], function(val, i, arr) { console.log(val, i, arr); //=> 1, 0, [ 1, 2, 3 ] //=> 2, 1, [ 1, 2, 3 ] if (i === 1) { return false; } });
savoy.each(collection, fn, done) — asynchronous each
savoy.each({ a: 1, b: 2, c: 3 }, function(cb, val, key, obj) {
console.log(val, key, obj);
//=> 1, 'a', { a: 1, b: 2, c: 3 }
//=> 2, 'b', { a: 1, b: 2, c: 3 }
//=> 3, 'c', { a: 1, b: 2, c: 3 }
cb();
}, function(err) {
console.log(err);
//=> null
});- The asynchronous function
fnis called in parallel over each item incollection. - Invoke the
cbcallback infnto signal the end of each iteration offn. - The signature of the
cbcallback iscb(err). Iferris truthy, thedonecallback is called exactly once with theerr. - When
fnhas completed execution over every item in thecollection, thedonecallback is called exactly once with a falsyerr.
eachSeries
savoy.eachSeries(collection, fn) — synchronous eachSeries
- Alias of
savoy.each.
savoy.eachSeries(collection, fn, done) — asynchronous eachSeries
savoy.each({ a: 1, b: 2, c: 3 }, function(cb, val, key, obj) {
console.log(val, key, obj);
//=> 1, 'a', { a: 1, b: 2, c: 3 }
//=> 2, 'b', { a: 1, b: 2, c: 3 }
//=> 3, 'c', { a: 1, b: 2, c: 3 }
cb();
}, function(err) {
console.log(err);
//=> null
});- The asynchronous function
fnis called in series over each item incollection. - Invoke the
cbcallback infnto signal the end of each iteration offn. - The signature of the
cbcallback iscb(err). Iferris truthy, we stop iterating over thecollection, and thedonecallback is called exactly once with theerr. - When
fnhas completed execution over every item in thecollection, thedonecallback is called exactly once with a falsyerr.
map
savoy.map(collection, fn) — synchronous map
var result = savoy.map({ a: 1, b: 2, c: 3 }, function(val, key, obj) {
console.log(val, key, obj);
//=> 1, 'a', { a: 1, b: 2, c: 3 }
//=> 2, 'b', { a: 1, b: 2, c: 3 }
//=> 3, 'c', { a: 1, b: 2, c: 3 }
return val * 2;
});
console.log(result);
//=> { a: 2, b: 4, c: 6 }savoy.map(collection, fn, done) — asynchronous map
savoy.map([1, 2, 3], function(cb, val, i, arr) {
console.log(val, i, arr);
//=> 1, 0, [1, 2, 3]
//=> 2, 1, [1, 2, 3]
//=> 3, 2, [1, 2, 3]
cb(null, val * 2);
}, function(err, result) {
console.log(err, result);
//=> null, [2, 4, 6]
});- The asynchronous function
fnis called in parallel over each item incollection. - Invoke the
cbcallback infnto signal the end of each iteration offn. The signature of thecbcallback iscb(err, mapVal):err— If truthy, thedonecallback is called exactly once with theerr.mapVal— This is accumulated in theresultargument of thedonecallback.
- When
fnhas completed execution over every item in thecollection, thedonecallback is called exactly once with a falsyerrand theresultof the map. - Note that if
collectionis an Object:resultwill also be an Object.- Items in
resultmay not be in the same order as their corresponding items in the originalcollection.
filter
savoy.filter(collection, fn) — synchronous filter
var result = savoy.filter([1, 2, 3], function(val, i, arr) {
console.log(val, i, arr);
//=> 1, 0, [1, 2, 3]
//=> 2, 1, [1, 2, 3]
//=> 3, 2, [1, 2, 3]
return val > 1;
});
console.log(result);
//=> [2, 3]savoy.filter(collection, fn, done) — asynchronous filter
savoy.filter({ a: 1, b: 2, c: 3 }, function(cb, val, key, obj) {
console.log(val, key, obj);
//=> 1, 'a', { a: 1, b: 2, c: 3 }
//=> 2, 'b', { a: 1, b: 2, c: 3 }
//=> 3, 'c', { a: 1, b: 2, c: 3 }
cb(null, val > 1);
}, function(err, result) {
console.log(err, result);
//=> null, { b: 2, c: 3 }
});- The asynchronous function
fnis called in parallel over each item incollection. - Invoke the
cbcallback infnto signal the end of each iteration offn. The signature of thecbcallback iscb(err, predicate):err— If truthy, thedonecallback is called exactly once with theerr.predicate— If truthy, the current item is added to theresultargument of thedonecallback.
- When
fnhas completed execution over every item in thecollection, thedonecallback is called exactly once with a falsyerrand theresultof the filter. - Note that if
collectionis an Object:resultwill also be an Object.- Items in
resultmay not be in the same relative order as they were incollection.
fold
savoy.fold(collection, acc, fn) — synchronous fold
var result = savoy.fold({ a: 1, b: 2, c: 3 }, 0, function(acc, val, key, obj) {
console.log(acc, val, key, obj);
//=> 1, 'a', { a: 1, b: 2, c: 3 }
//=> 2, 'b', { a: 1, b: 2, c: 3 }
//=> 3, 'c', { a: 1, b: 2, c: 3 }
return acc + val;
});
console.log(result);
//=> 6savoy.fold(collection, acc, fn, done) — asynchronous fold
savoy.fold([1, 2, 3], 0, function(cb, acc, val, i, arr) {
console.log(acc, val, i, arr);
//=> 0, 1, 0, [1, 2, 3]
//=> 1, 2, 1, [1, 2, 3]
//=> 3, 3, 2, [1, 2, 3]
cb(null, acc + val);
}, function(err, result) {
console.log(err, result);
//=> null, 6
});- The asynchronous function
fnis called in series over each item incollection. - Invoke the
cbcallback infnto signal the end of each iteration offn. The signature of thecbcallback iscb(err, foldVal):err— If truthy, thedonecallback is called exactly once with theerr.foldVal— This is the value foraccthat is passed to the next iteration offn.
- When
fnhas completed execution over every item in thecollection, thedonecallback is called exactly once with a falsyerrand theresultof the fold.
parallel
savoy.parallel(fns , done)
savoy.parallel([
function(cb) {
cb(null, 1);
},
function(cb) {
cb(null, 2);
},
function(cb) {
cb(null, 3);
}
], function(err, result) {
console.log(err, result);
//=> null, [1, 2, 3]
});- Each function in
fnsis called in parallel. - Invoke the
cbcallback in each function to signal the end of the function. The signature of thecbcallback iscb(err, val):err— If truthy, thedonecallback is called exactly once with theerr.val— This is accumulated in theresultargument of thedonecallback.
- When every function in
fnshas completed execution, thedonecallback is called exactly once with a falsyerrand theresultof each function.
series
savoy.series(fns , done)
savoy.series({
a: function(cb) {
cb(null, 1);
},
b: function(cb) {
cb(null, 2);
},
c: function(cb) {
cb(null, 3);
}
}, function(err, result) {
console.log(err, result);
//=> null, { a: 1, b: 2, c: 3 }
});- Each function in
fnsis called in series. - Invoke the
cbcallback in each function to signal the end of the function. The signature of thecbcallback iscb(err, val):err— If truthy, thedonecallback is called exactly once with theerr.val— This is accumulated in theresultargument of thedonecallback.
- When the entire series of functions has completed execution, the
donecallback is called exactly once with a falsyerrand theresultof each function.
waterfall
savoy.waterfall(fns , done)
savoy.waterfall([
function(cb) {
cb(null, 'a', 'b');
},
function(cb, arg1, arg2) {
console.log(arg1, arg2);
//=> 'a', 'b'
cb(null, 'c');
},
function(cb, arg) {
console.log(arg);
//=> 'c'
cb(null, 'd', 'e');
}
], function(err, arg1, arg2) {
console.log(err, arg1, arg2);
//=> null, 'd', 'e'
});- Each function in
fnsis called in series. - Invoke the
cbcallback in each function to signal the end of the function. The signature of thecbcallback iscb(err [, arg1, arg2, ...]):err— If truthy, thedonecallback is called exactly once with theerr.arg1, arg2, ...— Arguments that are passed on to the next function infns.
- When the entire series of functions has completed execution, the
donecallback is called exactly once with a falsyerrand arguments from the last function infns.
Installation
Install via npm:
$ npm i --save savoyInstall via bower:
$ bower i --save yuanqing/savoyTo use Savoy in the browser, include the minified script in your HTML:
<body>
<!-- ... -->
<script src="path/to/savoy.min.js"></script>
<script>
// savoy available here
</script>
</body>