diff --git a/.gitignore b/.gitignore
index 8225baa4a..1d9dd9d2d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
/node_modules
/dist
+/.idea
\ No newline at end of file
diff --git a/README.md b/README.md
index 17164760b..bffbbc8a8 100644
--- a/README.md
+++ b/README.md
@@ -73,6 +73,7 @@ So far its been tested in IE6, IE7, IE8, FF3.6 and Chrome 5. Usage:
### Collections
* [each](#each)
+* [eachIndex](#eachIndex)
* [map](#map)
* [filter](#filter)
* [reject](#reject)
@@ -131,7 +132,7 @@ __Arguments__
* arr - An array to iterate over.
* iterator(item, callback) - A function to apply to each item in the array.
The iterator is passed a callback(err) which must be called once it has
- completed. If no error has occured, the callback should be run without
+ 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 after all the iterator functions
have finished, or an error has occurred.
@@ -147,6 +148,47 @@ async.each(openFiles, saveFile, function(err){
});
```
+
+
+### eachIndex(arr, iterator, callback)
+
+Just like `each`, applies an iterator function to each item in an array,
+in parallel. The iterator is called with an item from the list, the position
+an item is at in the supplied array, and a callback for when it has finished.
+If the iterator passes an error to this callback, the main callback for the
+`eachIndex` 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.
+
+__Arguments__
+
+* arr - An array to iterate over.
+* iterator(item, index, callback) - A function to apply to each item in
+ the array. The iterator is passed an `index` position of the item in the
+ array. Useful when it is necessary to distinguish each item based on its
+ position in the array. Also 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 after all the iterator functions
+ have finished, or an error has occurred.
+
+__Example__
+
+```js
+var itemsArr = ['apple', 'banana', 'orange'];
+
+function iterator(val, index, callback) {
+ // do something with `val` based on its `index`,
+ // then call `callback`.
+}
+
+async.eachIndex(itemsArr, iterator, function(err){
+ // if any of the iterators produced an error, err would equal that error
+});
+```
+
---------------------------------------
@@ -177,7 +219,7 @@ __Arguments__
* limit - The maximum number of iterators to run at any time.
* iterator(item, callback) - A function to apply to each item in the array.
The iterator is passed a callback(err) which must be called once it has
- completed. If no error has occured, the callback should be run without
+ 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 after all the iterator functions
have finished, or an error has occurred.
diff --git a/lib/async.js b/lib/async.js
index ad3982a56..0ee8ca4a3 100755
--- a/lib/async.js
+++ b/lib/async.js
@@ -112,6 +112,29 @@
};
async.forEach = async.each;
+ async.eachIndex = function (arr, iterator, callback) {
+ callback = callback || function () {};
+ if (!arr.length) {
+ return callback();
+ }
+ var completed = 0;
+ _each(arr, function (x, ix) {
+ iterator(x, ix, only_once(function (err) {
+ if (err) {
+ callback(err);
+ callback = function () {};
+ }
+ else {
+ completed += 1;
+ if (completed >= arr.length) {
+ callback(null);
+ }
+ }
+ }));
+ });
+ };
+ async.forEachIndex = async.eachIndex;
+
async.eachSeries = function (arr, iterator, callback) {
callback = callback || function () {};
if (!arr.length) {
diff --git a/test/test-async.js b/test/test-async.js
index 5bd72bf4e..8b6d63967 100755
--- a/test/test-async.js
+++ b/test/test-async.js
@@ -17,6 +17,13 @@ function eachIterator(args, x, callback) {
}, x*25);
}
+function eachIndexIterator(args, x, ix, callback) {
+ setTimeout(function(){
+ args.push(x);
+ callback();
+ }, x*25);
+}
+
function mapIterator(call_order, x, callback) {
setTimeout(function(){
call_order.push(x);
@@ -43,6 +50,12 @@ function eachNoCallbackIterator(test, x, callback) {
test.done();
}
+function eachIndexNoCallbackIterator(test, x, ix, callback) {
+ test.equal(x, 1);
+ callback();
+ test.done();
+}
+
function getFunctionsObject(call_order) {
return {
one: function(callback){
@@ -824,6 +837,56 @@ exports['forEach alias'] = function (test) {
test.done();
};
+exports['eachIndex'] = function(test){
+ var args = [];
+ async.eachIndex([1,3,2], eachIndexIterator.bind(this, args), function(err){
+ test.same(args, [1,2,3]);
+ test.done();
+ });
+};
+
+exports['eachIndex extra callback'] = function(test){
+ var count = 0;
+ async.eachIndex([1,3,2], function(val, index, callback) {
+ count++;
+ callback();
+ test.throws(callback);
+ if (count == 3) {
+ test.done();
+ }
+ });
+};
+
+exports['eachIndex empty array'] = function(test){
+ test.expect(1);
+ async.eachIndex([], function(x, ix, callback){
+ test.ok(false, 'iterator should not be called');
+ callback();
+ }, function(err){
+ test.ok(true, 'should call callback');
+ });
+ setTimeout(test.done, 25);
+};
+
+exports['eachIndex error'] = function(test){
+ test.expect(1);
+ async.eachIndex([1,2,3], function(x, ix, callback){
+ callback('error');
+ }, function(err){
+ test.equals(err, 'error');
+ });
+ setTimeout(test.done, 50);
+};
+
+exports['eachIndex no callback'] = function(test){
+ async.eachIndex([1], eachIndexNoCallbackIterator.bind(this, test));
+};
+
+exports['forEachIndex alias'] = function (test) {
+ test.strictEqual(async.eachIndex, async.forEachIndex);
+ test.done();
+};
+
exports['eachSeries'] = function(test){
var args = [];
async.eachSeries([1,3,2], eachIterator.bind(this, args), function(err){