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
Control Flow
Events
Utils
Collections
### be.each(arr, iterator, callback)
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 in arr
.
The iterator is passed a callback(err)
which must be called once it has
completed. If no error has occurred, the callback
should be run without
arguments or with an explicit null
argument.callback(err)
- A callback which is called when all iterator
functions
have finished, or an error occurs.
Examples
be.each(openFiles, saveFile, function(err){
});
be.each(openFiles, function( file, callback) {
console.log('Processing file ' + file);
if( file.length > 32 ) {
console.log('This file name is too long');
callback('File name too long');
} else {
console.log('File processed');
callback();
}
}, function(err){
if( err ) {
console.log('A file failed to process');
} else {
console.log('All files have been processed successfully');
}
});
### be.eachSeries(arr, iterator, callback)
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.
### be.eachLimit(arr, limit, iterator, callback)
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 of iterator
s to run at any time.iterator(item, callback)
- A function to apply to each item in arr
.
The iterator is passed a callback(err)
which must be called once it has
completed. If no error has occurred, the callback should be run without
arguments or with an explicit null
argument.callback(err)
- A callback which is called when all iterator
functions
have finished, or an error occurs.
Example
be.eachLimit(documents, 20, requestApi, function(err){
});
### be.map(arr, iterator, callback)
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 in arr
.
The iterator is passed a callback(err, transformed)
which must be called once
it has completed with an error (which can be null
) and a transformed item.callback(err, results)
- A callback which is called when all iterator
functions have finished, or an error occurs. Results is an array of the
transformed items from the arr
.
Example
be.map(['file1','file2','file3'], fs.stat, function(err, results){
});
### be.mapSeries(arr, iterator, callback)
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.
### be.mapLimit(arr, limit, iterator, callback)
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 of iterator
s to run at any time.iterator(item, callback)
- A function to apply to each item in arr
.
The iterator is passed a callback(err, transformed)
which must be called once
it has completed with an error (which can be null
) and a transformed item.callback(err, results)
- A callback which is called when all iterator
calls have finished, or an error occurs. The result is an array of the
transformed items from the original arr
.
Example
be.mapLimit(['file1','file2','file3'], 1, fs.stat, function(err, results){
});
### be.filter(arr, iterator, callback)
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 in arr
.
The iterator
is passed a callback(err, truthValue)
, which must be called with a
boolean argument once it has completed.callback(err, results)
- A callback which is called after all the iterator
functions have finished.
Example
be.filter(['file1','file2','file3'], be.ccurry(fs.exists, 0, 0, null), function(err, results){
});
### be.filterSeries(arr, iterator, callback)
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.
### be.reject(arr, iterator, callback)
The opposite of filter
. Removes values that pass an async
truth test.
### be.rejectSeries(arr, iterator, callback)
The same as reject
, only the iterator
is applied to each item in arr
in series.
### be.reduce(arr, memo, iterator, callback)
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. The iterator
is passed a
callback(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 main callback
is
immediately called with the error.callback(err, result)
- A callback which is called after all the iterator
functions have finished. Result is the reduced value.
Example
be.reduce([1,2,3], 0, function(memo, item, callback){
process.nextTick(function(){
callback(null, memo + item)
});
}, function(err, result){
});
### be.reduceRight(arr, memo, iterator, callback)
Alias: foldr
Same as reduce
, only operates on arr
in reverse order.
### be.detect(arr, iterator, callback)
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 in arr
.
The iterator is passed a callback(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 returns
true
, or after all the iterator
functions have finished. Result will be
the first item in the array that passes the truth test (iterator) or the
value undefined
if none passed.
Example
be.detect(['file1','file2','file3'], be.ccurry(fs.exists, 0, 0, null), function(err, result){
});
### be.detectSeries(arr, iterator, callback)
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.
### be.sortBy(arr, iterator, callback)
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 in arr
.
The iterator is passed a callback(err, sortValue)
which must be called once it
has completed with an error (which can be null
) and a value to use as the sort
criteria.callback(err, results)
- A callback which is called after all the iterator
functions have finished, or an error occurs. Results is the items from
the original arr
sorted by the values returned by the iterator
calls.
Example
be.sortBy(['file1','file2','file3'], function(file, callback){
fs.stat(file, function(err, stats){
callback(err, stats.mtime);
});
}, function(err, results){
});
Sort Order
By modifying the callback parameter the sorting order can be influenced:
be.sortBy([1,9,3,5], function(x, callback){
callback(err, x);
}, function(err,result){
} );
be.sortBy([1,9,3,5], function(x, callback){
callback(err, x*-1);
}, function(err,result){
} );
### be.some(arr, iterator, 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 returns
true
, or after all the iterator functions have finished. Result will be
either true
or false
depending on the values of the async tests.
Example
be.some(['file1','file2','file3'], be.ccurry(fs.exists, 0, 0, null), function(err, result){
});
### be.every(arr, iterator, callback)
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 the iterator
functions have finished. Result will be either true
or false
depending on
the values of the async tests.
Example
be.every(['file1','file2','file3'], be.ccurry(fs.exists, 0, 0, null), function(err, result){
});
### be.concat(arr, iterator, callback)
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 in arr
.
The iterator is passed a callback(err, results)
which must be called once it
has completed with an error (which can be null
) and an array of results.callback(err, results)
- A callback which is called after all the iterator
functions have finished, or an error occurs. Results is an array containing
the concatenated results of the iterator
function.
Example
be.concat(['dir1','dir2','dir3'], fs.readdir, function(err, files){
});
### be.concatSeries(arr, iterator, callback)
Same as concat
, but executes in series instead of parallel.
Control Flow
### be.series(tasks, [callback])
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
a callback(err, result)
it must call on completion with an error err
(which can
be null
) and an optional result
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.series([
function(callback){
callback(null, 'one');
},
function(callback){
callback(null, 'two');
}
],
function(err, results){
});
be.series({
one: function(callback){
setTimeout(function(){
callback(null, 1);
}, 200);
},
two: function(callback){
setTimeout(function(){
callback(null, 2);
}, 100);
}
},
function(err, results) {
});
### be.parallel(tasks, [callback])
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
a callback(err, result)
which it must call on completion with an error err
(which can be null
) and an optional result
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);
}
],
function(err, results){
});
be.parallel({
one: function(callback){
setTimeout(function(){
callback(null, 1);
}, 200);
},
two: function(callback){
setTimeout(function(){
callback(null, 2);
}, 100);
}
},
function(err, results) {
});
### be.parallelLimit(tasks, limit, [callback])
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
a callback(err, result)
it must call on completion with an error err
(which can
be null
) and an optional result
value.limit
- The maximum number of tasks
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 the task
callbacks.
### be.waterfall(tasks, [callback])
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 a
callback(err, result1, result2, ...)
it must call on completion. The first
argument is an error (which can be null
) 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){
callback(null, 'three');
},
function(arg1, callback){
callback(null, 'done');
}
], function (err, result) {
});
### be.nextTick(callback)
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.push('one')
Events
### be.Emitter()
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);
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);
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);
### be.Channel(slots)
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);
});
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);
});
}
player('ping', table);
player('pong', table);
Utils
### be.curry(fn, pos, drop, args)
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);
require('fs').readFile('./index.js', be.curry(function(){
console.log(arguments);
}, 0, 1));
### be.ccurry(fn, pos, drop, args)
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);
});
var readFile = be.ccurry(require('fs').readFile, 0, 1);
readFile('./index.js', function(){
console.log(arguments);
});
### be.flip(table)
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));
assert.deepEqual(table, be.flip(be.flip(table)));
### be.unbind(fn, ...args)
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 ('));
### be.pipe(...fns)
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));
### be.merge(array)
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));
### be.once(fn)
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();
doSomethingOnce();
### be.noConflict()
Changes the value of be-async
back to its original value, returning a reference to the
be-async
object.