be-async
v0.1.8
Published
Control flows and methods to deal with asynchronous programs
Downloads
150
Maintainers
Readme
be-async
be-async
is a lighter version of the massively used async.js
module, if you don't know
what async.js
is, I recommend you to visit the project on Github
It's a lighter version as many methods have been removed, and the rest have been rewritten for sake of compactness and homogenity.
The goal of this module is to slowly grow from this de-facto standard which async.js
is,
implementing new methods and practical control flows.
Install
npm install be-async
Replacing async.js
This module can be a 'drop in replacement' of async.js
, but there are a few considerations:
Not every method is available. Mostly from the control flow section, I felt that many of these were not strictly necessary so I removed them. Please check twice if your project requires one of these.
All callbacks are required now to receive an error as the first argument.
async.js
wasn't doing this on the pretext of old Node.js truth methods not doing it too. You can always correct asynchronous methods that don't return errors using the utility 'ccurry', which can prefill callbacks with arguments in any position.Tests were ported from the original
async.js
, but obviously this module has not been used in the wild enough, so be careful, it may break (but it shouldn't).
This library now adds some functionality which is not present in async.js
:
- A basic event emitter and a channel abstraction built on top of it, golang style.
- Currying and callback currying functions, capable of dropping arguments too.
- Table flipping, also known as zip.
- Multi-dimensional array merge.
Documentation
The following is the original documentation of async.js
with some modifications.
Pull requests are very welcome!
Collections
each
eachSeries
eachLimit
map
mapSeries
mapLimit
filter
filterSeries
reject
rejectSeries
reduce
reduceRight
detect
detectSeries
sortBy
some
every
concat
concatSeries
Control Flow
Events
Utils
Collections
Applies the function iterator
to each item in arr
, in parallel.
The iterator
is called with an item from the list, and a callback for when it
has finished. If the iterator
passes an error to its callback
, the main
callback
(for the each
function) is immediately called with the error.
Note, that since this function applies iterator
to each item in parallel,
there is no guarantee that the iterator functions will complete in order.
Arguments
arr
- An array to iterate over.iterator(item, callback)
- A function to apply to each item inarr
. The iterator is passed acallback(err)
which must be called once it has completed. If no error has occurred, thecallback
should be run without arguments or with an explicitnull
argument.callback(err)
- A callback which is called when alliterator
functions have finished, or an error occurs.
Examples
// assuming openFiles is an array of file names and saveFile is a function
// to save the modified contents of that file:
be.each(openFiles, saveFile, function(err){
// if any of the saves produced an error, err would equal that error
});
// assuming openFiles is an array of file names
be.each(openFiles, function( file, callback) {
// Perform operation on file here.
console.log('Processing file ' + file);
if( file.length > 32 ) {
console.log('This file name is too long');
callback('File name too long');
} else {
// Do work to process file here
console.log('File processed');
callback();
}
}, function(err){
// if any of the file processing produced an error, err would equal that error
if( err ) {
// One of the iterations produced an error.
// All processing will now stop.
console.log('A file failed to process');
} else {
console.log('All files have been processed successfully');
}
});
The same as each
, only iterator
is applied to each item in arr
in
series. The next iterator
is only called once the current one has completed.
This means the iterator
functions will complete in order.
The same as each
, only no more than limit
iterator
s will be simultaneously
running at any time.
Note that the items in arr
are not processed in batches, so there is no guarantee that
the first limit
iterator
functions will complete before any others are started.
Arguments
arr
- An array to iterate over.limit
- The maximum number ofiterator
s to run at any time.iterator(item, callback)
- A function to apply to each item inarr
. The iterator is passed acallback(err)
which must be called once it has completed. If no error has occurred, the callback should be run without arguments or with an explicitnull
argument.callback(err)
- A callback which is called when alliterator
functions have finished, or an error occurs.
Example
// Assume documents is an array of JSON objects and requestApi is a
// function that interacts with a rate-limited REST api.
be.eachLimit(documents, 20, requestApi, function(err){
// if any of the saves produced an error, err would equal that error
});
Produces a new array of values by mapping each value in arr
through
the iterator
function. The iterator
is called with an item from arr
and a
callback for when it has finished processing. Each of these callback takes 2 arguments:
an error
, and the transformed item from arr
. If iterator
passes an error to his
callback, the main callback
(for the map
function) is immediately called with the error.
Note, that since this function applies the iterator
to each item in parallel,
there is no guarantee that the iterator
functions will complete in order.
However, the results array will be in the same order as the original arr
.
Arguments
arr
- An array to iterate over.iterator(item, callback)
- A function to apply to each item inarr
. The iterator is passed acallback(err, transformed)
which must be called once it has completed with an error (which can benull
) and a transformed item.callback(err, results)
- A callback which is called when alliterator
functions have finished, or an error occurs. Results is an array of the transformed items from thearr
.
Example
be.map(['file1','file2','file3'], fs.stat, function(err, results){
// results is now an array of stats for each file
});
The same as map
, only the iterator
is applied to each item in arr
in
series. The next iterator
is only called once the current one has completed.
The results array will be in the same order as the original.
The same as map
, only no more than limit
iterator
s will be simultaneously
running at any time.
Note that the items are not processed in batches, so there is no guarantee that
the first limit
iterator
functions will complete before any others are started.
Arguments
arr
- An array to iterate over.limit
- The maximum number ofiterator
s to run at any time.iterator(item, callback)
- A function to apply to each item inarr
. The iterator is passed acallback(err, transformed)
which must be called once it has completed with an error (which can benull
) and a transformed item.callback(err, results)
- A callback which is called when alliterator
calls have finished, or an error occurs. The result is an array of the transformed items from the originalarr
.
Example
be.mapLimit(['file1','file2','file3'], 1, fs.stat, function(err, results){
// results is now an array of stats for each file
});
Alias: select
Returns a new array of all the values in arr
which pass an be truth test.
This operation is performed in parallel, but the results array will
be in the same order as the original.
This method has been 'corrected' from the original version, the callbacks
now both accept an error object and a correction with ccurry
should be done to functions that don't call back with initial error arguments.
Arguments
arr
- An array to iterate over.iterator(item, callback)
- A truth test to apply to each item inarr
. Theiterator
is passed acallback(err, truthValue)
, which must be called with a boolean argument once it has completed.callback(err, results)
- A callback which is called after all theiterator
functions have finished.
Example
be.filter(['file1','file2','file3'], be.ccurry(fs.exists, 0, 0, null), function(err, results){
// results now equals an array of the existing files
});
Alias: selectSeries
The same as filter
only the iterator
is applied to each item in arr
in
series. The next iterator
is only called once the current one has completed.
The results array will be in the same order as the original.
The opposite of filter
. Removes values that pass an async
truth test.
The same as reject
, only the iterator
is applied to each item in arr
in series.
Aliases: inject
, foldl
Reduces arr
into a single value using an async iterator
to return
each successive step. memo
is the initial state of the reduction.
This function only operates in series.
For performance reasons, it may make sense to split a call to this function into
a parallel map, and then use the normal Array.prototype.reduce
on the results.
This function is for situations where each step in the reduction needs to be async;
if you can get the data before reducing it, then it's probably a good idea to do so.
Arguments
arr
- An array to iterate over.memo
- The initial state of the reduction.iterator(memo, item, callback)
- A function applied to each item in the array to produce the next step in the reduction. Theiterator
is passed acallback(err, reduction)
which accepts an optional error as its first argument, and the state of the reduction as the second. If an error is passed to the callback, the reduction is stopped and the maincallback
is immediately called with the error.callback(err, result)
- A callback which is called after all theiterator
functions have finished. Result is the reduced value.
Example
be.reduce([1,2,3], 0, function(memo, item, callback){
// pointless async:
process.nextTick(function(){
callback(null, memo + item)
});
}, function(err, result){
// result is now equal to the last value of memo, which is 6
});
Alias: foldr
Same as reduce
, only operates on arr
in reverse order.
Returns the first value in arr
that passes an be truth test. The
iterator
is applied in parallel, meaning the first iterator to return true
will
fire the detect callback
with that result. That means the result might not be
the first item in the original arr
(in terms of order) that passes the test.
This method has been 'corrected' from the original version, the callbacks
now both accept an error object and a correction with ccurry
should be done to functions that don't call back with initial error arguments.
If order within the original arr
is important, then look at detectSeries
.
Arguments
arr
- An array to iterate over.iterator(item, callback)
- A truth test to apply to each item inarr
. The iterator is passed acallback(truthValue)
which must be called with a boolean argument once it has completed.callback(err, result)
- A callback which is called as soon as any iterator returnstrue
, or after all theiterator
functions have finished. Result will be the first item in the array that passes the truth test (iterator) or the valueundefined
if none passed.
Example
be.detect(['file1','file2','file3'], be.ccurry(fs.exists, 0, 0, null), function(err, result){
// result now equals the first file in the list that exists
});
The same as detect
, only the iterator
is applied to each item in arr
in series. This means the result is always the first in the original arr
(in
terms of array order) that passes the truth test.
Sorts a list by the results of running each arr
value through an be iterator
.
Arguments
arr
- An array to iterate over.iterator(item, callback)
- A function to apply to each item inarr
. The iterator is passed acallback(err, sortValue)
which must be called once it has completed with an error (which can benull
) and a value to use as the sort criteria.callback(err, results)
- A callback which is called after all theiterator
functions have finished, or an error occurs. Results is the items from the originalarr
sorted by the values returned by theiterator
calls.
Example
be.sortBy(['file1','file2','file3'], function(file, callback){
fs.stat(file, function(err, stats){
callback(err, stats.mtime);
});
}, function(err, results){
// results is now the original array of files sorted by
// modified date
});
Sort Order
By modifying the callback parameter the sorting order can be influenced:
//ascending order
be.sortBy([1,9,3,5], function(x, callback){
callback(err, x);
}, function(err,result){
//result callback
} );
//descending order
be.sortBy([1,9,3,5], function(x, callback){
callback(err, x*-1); //<- x*-1 instead of x, turns the order around
}, function(err,result){
//result callback
} );
Alias: any
Returns true
if at least one element in the arr
satisfies an async test.
Once any iterator call returns true
, the main callback
is immediately called.
This method has been 'corrected' from the original version, the callbacks
now both accept an error object and a correction with ccurry
should be done to functions that don't call back with initial error arguments.
Arguments
arr
- An array to iterate over.iterator(item, callback)
- A truth test to apply to each item in the array in parallel. The iterator is passed a callback(err, truthValue) which must be called with a boolean argument once it has completed.callback(err, result)
- A callback which is called as soon as any iterator returnstrue
, or after all the iterator functions have finished. Result will be eithertrue
orfalse
depending on the values of the async tests.
Example
be.some(['file1','file2','file3'], be.ccurry(fs.exists, 0, 0, null), function(err, result){
// if result is true then at least one of the files exists
});
Alias: all
Returns true
if every element in arr
satisfies an async test.
This method has been 'corrected' from the original version, the callbacks
now both accept an error object and a correction with ccurry
should be done to functions that don't call back with initial error arguments.
Arguments
arr
- An array to iterate over.iterator(item, callback)
- A truth test to apply to each item in the array in parallel. The iterator is passed a callback(err, truthValue) which must be called with a boolean argument once it has completed.callback(err, result)
- A callback which is called after all theiterator
functions have finished. Result will be eithertrue
orfalse
depending on the values of the async tests.
Example
be.every(['file1','file2','file3'], be.ccurry(fs.exists, 0, 0, null), function(err, result){
// if result is true then every file exists
});
Applies iterator
to each item in arr
, concatenating the results. Returns the
concatenated list. The iterator
s are called in parallel, and the results are
concatenated as they return. This method now guarantees original array order in
the concatenated results.
Arguments
arr
- An array to iterate over.iterator(item, callback)
- A function to apply to each item inarr
. The iterator is passed acallback(err, results)
which must be called once it has completed with an error (which can benull
) and an array of results.callback(err, results)
- A callback which is called after all theiterator
functions have finished, or an error occurs. Results is an array containing the concatenated results of theiterator
function.
Example
be.concat(['dir1','dir2','dir3'], fs.readdir, function(err, files){
// files is now a list of filenames that exist in the 3 directories
});
Same as concat
, but executes in series instead of parallel.
Control Flow
Run the functions in the tasks
array in series, each one running once the previous
function has completed. If any functions in the series pass an error to its
callback, no more functions are run, and callback
is immediately called with the value of the error.
Otherwise, callback
receives an array of results when tasks
have completed.
It is also possible to use an object instead of an array. Each property will be
run as a function, and the results will be passed to the final callback
as an object
instead of an array. This can be a more readable way of handling results from
series
.
Note that while many implementations preserve the order of object properties, the ECMAScript Language Specifcation explicitly states that
The mechanics and order of enumerating the properties is not specified.
So if you rely on the order in which your series of functions are executed, and want this to work on all platforms, consider using an array.
Arguments
tasks
- An array or object containing functions to run, each function is passed acallback(err, result)
it must call on completion with an errorerr
(which can benull
) and an optionalresult
value.callback(err, results)
- An optional callback to run once all the functions have completed. This function gets a results array (or object) containing all the result arguments passed to thetask
callbacks.
Example
be.series([
function(callback){
// do some stuff ...
callback(null, 'one');
},
function(callback){
// do some more stuff ...
callback(null, 'two');
}
],
// optional callback
function(err, results){
// results is now equal to ['one', 'two']
});
// an example using an object instead of an array
be.series({
one: function(callback){
setTimeout(function(){
callback(null, 1);
}, 200);
},
two: function(callback){
setTimeout(function(){
callback(null, 2);
}, 100);
}
},
function(err, results) {
// results is now equal to: {one: 1, two: 2}
});
Run the tasks
array of functions in parallel, without waiting until the previous
function has completed. If any of the functions pass an error to its
callback, the main callback
is immediately called with the value of the error.
Once the tasks
have completed, the results are passed to the final callback
as an
array.
It is also possible to use an object instead of an array. Each property will be
run as a function and the results will be passed to the final callback
as an object
instead of an array. This can be a more readable way of handling results from
parallel
.
Arguments
tasks
- An array or object containing functions to run. Each function is passed acallback(err, result)
which it must call on completion with an errorerr
(which can benull
) and an optionalresult
value.callback(err, results)
- An optional callback to run once all the functions have completed. This function gets a results array (or object) containing all the result arguments passed to the task callbacks.
Example
be.parallel([
function(callback){
setTimeout(function(){
callback(null, 'one');
}, 200);
},
function(callback){
setTimeout(function(){
callback(null, 'two');
}, 100);
}
],
// optional callback
function(err, results){
// the results array will equal ['one','two'] even though
// the second function had a shorter timeout.
});
// an example using an object instead of an array
be.parallel({
one: function(callback){
setTimeout(function(){
callback(null, 1);
}, 200);
},
two: function(callback){
setTimeout(function(){
callback(null, 2);
}, 100);
}
},
function(err, results) {
// results is now equals to: {one: 1, two: 2}
});
The same as parallel
, only tasks
are executed in parallel
with a maximum of limit
tasks executing at any time.
Note that the tasks
are not executed in batches, so there is no guarantee that
the first limit
tasks will complete before any others are started.
Arguments
tasks
- An array or object containing functions to run, each function is passed acallback(err, result)
it must call on completion with an errorerr
(which can benull
) and an optionalresult
value.limit
- The maximum number oftasks
to run at any time.callback(err, results)
- An optional callback to run once all the functions have completed. This function gets a results array (or object) containing all the result arguments passed to thetask
callbacks.
Runs the tasks
array of functions in series, each passing their results to the next in
the array. However, if any of the tasks
pass an error to their own callback, the
next function is not executed, and the main callback
is immediately called with
the error.
Arguments
tasks
- An array of functions to run, each function is passed acallback(err, result1, result2, ...)
it must call on completion. The first argument is an error (which can benull
) and any further arguments will be passed as arguments in order to the next task.callback(err, [results])
- An optional callback to run once all the functions have completed. This will be passed the results of the last task's callback.
Example
be.waterfall([
function(callback){
callback(null, 'one', 'two');
},
function(arg1, arg2, callback){
// arg1 now equals 'one' and arg2 now equals 'two'
callback(null, 'three');
},
function(arg1, callback){
// arg1 now equals 'three'
callback(null, 'done');
}
], function (err, result) {
// result now equals 'done'
});
Calls callback
on a later loop around the event loop. In Node.js this just
calls process.nextTick
; in the browser it falls back to setImmediate(callback)
if available, otherwise setTimeout(callback, 0)
, which means other higher priority
events may precede the execution of callback
.
This is used internally for browser-compatibility purposes.
Arguments
callback
- The function to call on a later loop around the event loop.
Example
var call_order = [];
be.nextTick(function(){
call_order.push('two');
// call_order now equals ['one','two']
});
call_order.push('one')
Events
Basic event emitter.
be.Emitter.on(type, callback)
Calls callback
when an event of the supplied type
is emitter by Emitter.emit()
.
Bound events are stored in a queue where each will be called asynchronously in the
same order they were added. User must take care of removing them using Emitter.off()
when they are not used anymore.
Arguments
type
- The type of the event as a string.callback
- The function that will be called when this event is triggered.
Example
var call_order = [];
var emitter = new be.Emitter;
emitter.on('event', function(n){
call_order.push(n);
});
emitter.on('event', function(n){
call_order.push(n + 1);
});
emitter.emit('event', 1); // call_order now equals [1, 2]
be.Emitter.once(type, callback)
Same as Emitter.on()
but callback will be called just once and
then automatically removed from the queue.
be.Emitter.off(type, callback)
Removes callback
from the event type
queue.
Arguments
type
- The type of the event as a string.callback
- The function that will be removed from the queue (must be externally referenced).
Example
var call_order = [];
var emitter = new be.Emitter;
var add = function(n){
call_order.push(n);
};
emitter.on('event', add);
emitter.off('event', add);
emitter.emit('event', 1); // call_order now equals []
be.Emitter.emit(type, ...args)
Emits an event of the supplied type
with the given variadic args
.
Arguments
type
- The type of the event as a string.args
- Variadic arguments which will be passed to the callback registered in the event type queue.
Example
var call_order = [];
var emitter = new be.Emitter;
var add = function(n){
call_order.push(n);
};
emitter.on('event', add);
emitter.emit('event', 1); // call_order now equals [1]
Basic evented channel, analog to golang channels.
Arguments
slots
- Number of lat data slots which will be kept in a buffer while they cannot be delivered (no shifters).
be.Channel.push(...args)
Pushes an arbitrary number of data args
to the channel.
Arguments
args
- Variadic arguments which will be writen at once in the channel queue.
Example
var chan = new be.Channel;
chan.push({ sample: "data" }, { some: "more" });
chan.shift(function(){
console.log(arguments); // { '0': { sample: "data" }, '1': { some: "more" } }
});
be.Channel.shift(callback)
Reads data from the channel, passing it as variadic arguments to callback
. Once the data is consumed
once it is removed from the channel.
Arguments
callback
- The function consuming the data shifted from the channel.
Example
var ball = { hits: 0 };
var table = new Channel(1);
table.push(Object.create(ball));
setTimeout(table.push.bind(table), 1000);
function player(name, table){
table.shift(function(ball){
ball.hits++;
console.log(name, ball.hits);
setTimeout(table.push.bind(table, ball), 100);
});
}
// When using a buffered Channel, one can add listeners
// after pushing data
player('ping', table);
player('pong', table);
Utils
Positional curry function. Returns a function fn
with prefilled argument(s) args
overwriting a number of arguments drop
in position pos
.
Arguments
fn
- Function to be prefilled with arguments.pos
- Postion where the arguments should be placed.drop
- Number of arguments that will be overwritten or dropped.args
- Single value or array of arguments which will be applied to the curried function.
Example
var divide = function(a, b){
return a / b
};
var half = be.curry(divide, 1, 0, 2);
half(6); // => 3
// dropping arguments
require('fs').readFile('./index.js', be.curry(function(){
console.log(arguments); // { 0: <contents of index.js> }
}, 0, 1));
Continuation positional curry function. Returns a funcion fn
with a prefilled callback
which will overwrite a number drop
of arguments with a new set args
in position pos
.
Arguments
fn
- Function to be prefilled with a prefilled callback.pos
- Postion where the arguments should be placed in the callback.drop
- Number of arguments that will be overwritten or dropped.args
- Single value or array of arguments which will be applied to the curried function.
Example
var exists = be.ccurry(require('fs').exists, 0, 0, null);
exists('./index.js', function(){
console.log(arguments); // { 0: null, 1: true }
});
// dropping arguments
var readFile = be.ccurry(require('fs').readFile, 0, 1);
readFile('./index.js', function(){
console.log(arguments); // { 0: <contents of index.js> }
});
Flips a table
(changes columns to rows and viceversa), which is
represented as an array containing a set of arrays.
Arguments
table
- An array of arrays, representing rows and columns.
Example
var table = [
[1,2,3],
[4,5,6]
];
console.log(flip(table)); // [[1,4],[2,5],[3,6]]
assert.deepEqual(table, be.flip(be.flip(table))); // double flip returns it to original state
Does the opposite of Function.prototype.bind
. It removes the function it's default context,
leaving it for the user to pass it as the first argument. Useful to reuse standard library
prototypal functions like String.prototype.replace
. Like Function.prototype.bind
, it
takes variadic args
which will be curried over the function returned.
Arguments
fn
- Function to be unbound.args
- Variadic arguments which will be passed to the resulting function.
Example
var readFile = be.ccurry(require('fs').readFile, 0, 1);
readfile('./index.js',
be.unbind(String.prototype.replace, 'function(', 'function ('));
Returns a function which will pass it's argument to a series of piped variadic functions fns
.
The resulting function call is the verse of function composing, so it's more natural to read.
c(b(a(val))) -> be.pipe(a, b, c)(val);
Arguments
fns
- Variadic function to be piped.
Example
var stat = be.ccurry(require('fs').stat, 0, 1);
var beautify = be.curry(JSON.stringify, 1, 0, [null, 4]);
stat('./index.js', be.pipe(beautify, console.log));
Merges subarrays into one big array. The equivalent of performing Array.concat on each subarray against the previous one.
Arguments
array
- Array containing subarrays.
Example
var arr = [[1], [2, 3], [4, 5, 6]];
console.log(be.merge(arr)); // => [1, 2, 3, 4, 5, 6]
Returns a function which calls the provided function fn
just once. For the next calls,
it will be performing no operation.
Arguments
fn
- Function to prevent calling more than once.
Example
var doSomething = function(){
console.log('beep');
}
var doSomethingOnce = be.once(doSomething);
doSomethingOnce(); // => beep
doSomethingOnce(); // =>
Changes the value of be-async
back to its original value, returning a reference to the
be-async
object.