reduceAsync
Apply a function against an accumulator and each element in a collection and return the accumulated result.
Usage
var reduceAsync = require( '@stdlib/utils/async/reduce' );
reduceAsync( collection, initial, [options,] reducer, done )
Applies a function against an accumulator and each element in a collection
and returns the accumulated result.
function reducer( acc, value, index, next ) {
setTimeout( onTimeout, value );
function onTimeout() {
console.log( value );
acc.sum += value;
next( null, acc );
}
}
function done( error, acc ) {
if ( error ) {
throw error;
}
console.log( acc.sum );
}
var arr = [ 3000, 2500, 1000 ];
var acc = {
'sum': 0
};
reduceAsync( arr, acc, reducer, done );
/*
3000
2500
1000
6500
*/
The next
callback accepts two arguments: error
and accumulator
. The second argument to the next
callback is passed as the first argument to the provided reducer
.
function reducer( acc, value, index, next ) {
setTimeout( onTimeout, value );
function onTimeout() {
next( null, acc );
}
}
function done( error, out ) {
if ( error ) {
throw error;
}
console.log( out === acc );
// => true
}
var arr = [ 3000, 2500, 1000 ];
var acc = {};
reduceAsync( arr, acc, reducer, done );
The function accepts the following options
:
- limit: the maximum number of pending invocations at any one time. If provided, the function sets
options.series=false
. Default:infinity
. - series: boolean indicating whether to sequentially invoke
reducer
for eachcollection
element. Iftrue
, the function setsoptions.limit=1
. Default:true
. - thisArg: the execution context for
reducer
.
By default, all elements are processed sequentially, which means that the function does guarantee completion order. To process each collection
element concurrently, set the series
option to false
.
function reducer( acc, value, index, next ) {
setTimeout( onTimeout, value );
function onTimeout() {
console.log( value );
acc.sum += value;
next( null, acc );
}
}
function done( error, acc ) {
if ( error ) {
throw error;
}
console.log( acc.sum );
}
var arr = [ 3000, 2500, 1000 ];
var acc = {
'sum': 0
};
var opts = {
'series': false
};
reduceAsync( arr, acc, opts, reducer, done );
/* =>
1000
2500
3000
6500
*/
To limit the maximum number of pending function invocations, set the limit
option.
function reducer( acc, value, index, next ) {
setTimeout( onTimeout, value );
function onTimeout() {
console.log( value );
acc.sum += value;
next( null, acc );
}
}
function done( error, acc ) {
if ( error ) {
throw error;
}
console.log( acc.sum );
}
var arr = [ 3000, 2500, 1000 ];
var acc = {
'sum': 0
};
var opts = {
'limit': 2
};
reduceAsync( arr, acc, opts, reducer, done );
/* =>
2500
3000
1000
6500
*/
To set the execution context of reducer
, set the thisArg
option.
function reducer( acc, value, index, next ) {
this.count += 1;
setTimeout( onTimeout, value );
function onTimeout() {
acc.sum += value;
next( null, acc );
}
}
var arr = [ 3000, 2500, 1000 ];
var acc = {
'sum': 0
};
var context = {
'count': 0
};
var opts = {
'thisArg': context
};
reduceAsync( arr, acc, opts, reducer, done );
function done( error, acc ) {
if ( error ) {
throw error;
}
console.log( acc.sum );
// => 6500
console.log( context.count );
// => 3
}
When invoked, reducer
is provided a maximum of five arguments:
- accumulator: accumulated value.
- value: collection value.
- index: collection index.
- collection: the input
collection
. - next: a callback which should be called once
reducer
has finished processing a collectionvalue
.
The actual number of provided arguments depends on function length
. If reducer
accepts three arguments, reducer
is provided accumulator
, value
and next
. If reducer
accepts four arguments, reducer
is provided accumulator
, value
, index
, and next
. For every other reducer
signature, reducer
is provided all five arguments.
function reducer( acc, value, i, collection, next ) {
console.log( 'collection: %s. %d: %d', collection.join( ',' ), i, value );
setTimeout( onTimeout, value );
function onTimeout() {
console.log( value );
acc.sum += value;
next( null, acc );
}
}
function done( error, acc ) {
if ( error ) {
throw error;
}
console.log( acc.sum );
}
var arr = [ 3000, 2500, 1000 ];
var acc = {
'sum': 0
};
reduceAsync( arr, acc, reducer, done );
/* =>
collection: 3000,2500,1000. 0: 3000
collection: 3000,2500,1000. 1: 2500
collection: 3000,2500,1000. 2: 1000
3000
2500
1000
6500
*/
reduceAsync.factory( [options,] reducer )
Returns a function which invokes a function once for each element in a collection
.
function reducer( acc, value, index, next ) {
setTimeout( onTimeout, value );
function onTimeout() {
console.log( value );
acc.sum += value;
next( null, acc );
}
}
function done( error, acc ) {
if ( error ) {
throw error;
}
console.log( acc.sum );
}
var f = reduceAsync.factory( reducer );
var arr1 = [ 3000, 2500, 1000 ];
var acc1 = {
'sum': 0
};
f( arr1, acc1, done );
/* =>
3000
2500
1000
6500
*/
var arr2 = [ 300, 250, 100 ];
var acc2 = {
'sum': 0
};
f( arr2, acc2, done );
/* =>
300
250
100
650
*/
The function accepts the same options
as reduceAsync()
.
Notes
- A
collection
may be either anArray
,Typed Array
, or an array-likeObject
(excludingstrings
andfunctions
). - If a provided function calls the
next
callback with a truthyerror
argument, the function suspends execution and immediately calls thedone
callback for subsequenterror
handling. - The function invokes the
done
callback with theaccumulator
provided as the second argument. If provided an emptycollection
, the function invokes thedone
callback with theinitial
value as the second argument. - The function does not support dynamic
collection
resizing. - The function does not skip
undefined
elements. - When processing
collection
elements concurrently, beware of race conditions when updating an accumulator. This is especially true when an accumulator is a primitive (e.g., anumber
). In general, preferobject
accumulators, as objects are passed by reference, not by value. - Neither
reduceAsync
nor the function returned by thefactory
method guarantee asynchronous execution. To guarantee asynchrony, wrap thedone
callback in a function which either executes at the end of the current stack (e.g.,nextTick
) or during a subsequent turn of the event loop (e.g.,setImmediate
,setTimeout
).
Examples
var resolve = require( 'path' ).resolve;
var readFile = require( '@stdlib/fs/read-file' );
var reduceAsync = require( '@stdlib/utils/async/reduce' );
var files = [
resolve( __dirname, 'package.json' ),
resolve( __dirname, 'README.md' )
];
function done( error, acc ) {
if ( error ) {
throw error;
}
console.log( acc.count );
}
function read( acc, file, next ) {
var opts = {
'encoding': 'utf8'
};
readFile( file, opts, onFile );
function onFile( error ) {
if ( error ) {
return next( null, acc );
}
acc.count += 1;
next( null, acc );
}
}
var acc = {
'count': 0
};
reduceAsync( files, acc, read, done );