tiny-each-async
Advanced tools
Comparing version 1.1.0 to 2.0.0
70
index.js
@@ -0,35 +1,57 @@ | ||
/* eslint-disable no-use-before-define */ | ||
'use strict'; | ||
// we don't need a real shim setImmediate | ||
var immediate = (typeof setImmediate === 'function') ? setImmediate : setTimeout; | ||
module.exports = function eachAsync(arr, parallelLimit, iteratorFn, cb) { | ||
var pending = 0; | ||
var index = 0; | ||
var lastIndex = arr.length - 1; | ||
var called = false; | ||
var limit; | ||
var callback; | ||
var iterate; | ||
module.exports = function eachAsync(arr, iterator, cb) { | ||
if (!Array.isArray(arr)) { | ||
throw new TypeError('Invalid first argument! Expecting array!'); | ||
if (typeof parallelLimit === 'number') { | ||
limit = parallelLimit; | ||
iterate = iteratorFn; | ||
callback = cb || function noop() {}; | ||
} else { | ||
iterate = parallelLimit; | ||
callback = iteratorFn || function noop() {}; | ||
limit = arr.length; | ||
} | ||
if (typeof iterator !== 'function') { | ||
throw new TypeError('Invalid second argument! Expecting function!'); | ||
} | ||
var iteratorLength = iterate.length; | ||
var callback = cb || function noop() {}; | ||
var counter = arr.length; | ||
var iteratorLength = iterator.length; | ||
var error; | ||
var shouldCallNextIterator = function shouldCallNextIterator() { | ||
return ((pending < limit) && (index < lastIndex)); | ||
}; | ||
arr.forEach(function forEach(item, index) { | ||
var next = function next(err) { | ||
if (!error && (err || !--counter)) { | ||
error = err; | ||
var iteratorCallback = function iteratorCallback(err) { | ||
if (called) { return; } | ||
immediate(function asyncify() { | ||
callback(err); | ||
}, 0); | ||
} | ||
}; | ||
pending--; | ||
var args = (iteratorLength === 2) ? [item, next] : [item, index, next]; | ||
if (err || (index === lastIndex && !pending)) { | ||
called = true; | ||
iterator.apply(null, args); | ||
}); | ||
callback(err); | ||
} else if (shouldCallNextIterator()) { | ||
processIterator(++index); | ||
} | ||
}; | ||
var processIterator = function processIterator() { | ||
pending++; | ||
var args = (iteratorLength === 2) ? [arr[index], iteratorCallback] | ||
: [arr[index], index, iteratorCallback]; | ||
iterate.apply(null, args); | ||
if (shouldCallNextIterator()) { | ||
processIterator(++index); | ||
} | ||
}; | ||
processIterator(); | ||
}; |
{ | ||
"name": "tiny-each-async", | ||
"version": "1.1.0", | ||
"version": "2.0.0", | ||
"description": "Asynchronous iterator function for parallel processing.", | ||
@@ -23,6 +23,10 @@ "main": "index.js", | ||
"alessioalex-standard": "^1.0.0", | ||
"async": "^1.5.0", | ||
"husky": "^0.10.1", | ||
"lolex": "^1.3.2", | ||
"matcha": "^0.6.0", | ||
"tape": "^4.2.2" | ||
}, | ||
"scripts": { | ||
"bench": "matcha bench/*.js", | ||
"test": "tape test.js", | ||
@@ -29,0 +33,0 @@ "lint": "alessioalex-standard", |
# tiny-each-async | ||
Asynchronous iterator function, similar to (and inspired by) [async.each](https://github.com/caolan/async#eacharr-iterator-callback). | ||
Asynchronous iterator function similar to (and inspired by) [async.each](https://github.com/caolan/async#eacharr-iterator-callback), with support for concurrency limit and item index. | ||
@@ -14,2 +14,3 @@ [![build status](https://secure.travis-ci.org/alessioalex/tiny-each-async.png)](http://travis-ci.org/alessioalex/tiny-each-async) | ||
- array - An array of items to iterate through. | ||
- [limit] - An (optional) integer for determining how many `iterator` functions should be run in parallel. | ||
- iterator(item, [index], callback) - A function to be applied to each item in the array. When it has finished processing the item then the `callback` function should be called (in case of a failure with the `error` argument, otherwise none). | ||
@@ -34,2 +35,4 @@ - callback(err) - An optional callback function that gets called when either all `iterator` functions have finished or one of them has returned an error. | ||
For more examples checkout the [/examples](/tree/master/examples) folder. | ||
## FAQ | ||
@@ -45,4 +48,8 @@ | ||
- What if my iterator function is sync, but I want it && the callback to be async? | ||
Then you might want to use [dezalgo](https://github.com/npm/dezalgo). | ||
## License | ||
[MIT](http://alessioalex.mit-license.org/) |
48
test.js
@@ -6,2 +6,3 @@ /* eslint-disable no-console, func-names */ | ||
var eachAsync = require('./'); | ||
var lolex = require('lolex'); | ||
@@ -20,15 +21,2 @@ it('should execute the final callback once all individual tasks are finished', function(t) { | ||
it('should be async even though the iterator is not', function(t) { | ||
var index = 0; | ||
eachAsync([1, 2, 3], function(item, i, next) { | ||
next(); | ||
}, function() { | ||
t.equal(++index, 2); | ||
t.end(); | ||
}); | ||
t.equal(++index, 1); | ||
}); | ||
it('should provide index as an argument for the iterator if needed', function(t) { | ||
@@ -54,2 +42,14 @@ var items = [11, 22, 33]; | ||
it('should treat limit as an optional param', function(t) { | ||
eachAsync([1, 2, 3], function(item, next) { | ||
next(); | ||
}, function() { | ||
eachAsync([1, 2, 3], 2, function(item, next) { | ||
next(); | ||
}, function() { | ||
t.end(); | ||
}); | ||
}); | ||
}); | ||
it('should return early in case there\'s an error', function(t) { | ||
@@ -67,1 +67,23 @@ var error = new Error('test'); | ||
}); | ||
it('should limit the concurrency', function(t) { | ||
var clock = lolex.install(); | ||
var items = []; | ||
eachAsync([1, 2, 3, 4, 5], 2, function(item, next) { | ||
setTimeout(function() { | ||
items.push(item); | ||
next(); | ||
}, 1000); | ||
}, function() { | ||
clock.uninstall(); | ||
t.end(); | ||
}); | ||
clock.tick(1001); | ||
t.deepEqual([1, 2], items); | ||
clock.tick(1001); | ||
t.deepEqual([1, 2, 3, 4], items); | ||
clock.tick(1000); | ||
t.deepEqual([1, 2, 3, 4, 5], items); | ||
}); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
5897
115
53
6
6