diff --git a/README.md b/README.md index 9ff1acfdf..46869a469 100644 --- a/README.md +++ b/README.md @@ -150,7 +150,7 @@ So far it's been tested in IE6, IE7, IE8, FF3.6 and Chrome 5. Usage: ### each(arr, iterator, callback) -Applies an iterator function to each item in an array, in parallel. +Applies an iterator function to each item in an array or object, 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 this callback, the main callback for the each function is immediately called with the error. @@ -160,8 +160,8 @@ 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 the array. +* arr - An array/object to iterate over. +* iterator(item [, key], callback) - A function to apply to each item in the array/object. 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 arguments or with an explicit null argument. diff --git a/lib/async.js b/lib/async.js index 46f4f509e..0582db5cf 100755 --- a/lib/async.js +++ b/lib/async.js @@ -28,6 +28,13 @@ //// cross-browser compatiblity functions //// var _each = function (arr, iterator) { + if (!(arr instanceof Array) && typeof arr === 'object') { + for (var key in arr) { + if (Object.prototype.hasOwnProperty.call(arr, key)) + iterator(arr[key], key, arr); + } + return; + } if (arr.forEach) { return arr.forEach(iterator); } @@ -70,6 +77,12 @@ return keys; }; + var _length = function (arr) { + if (arr instanceof Array) return arr.length; + if (Object.keys) return Object.keys(arr).length; + return _keys(arr).length; + } + //// exported async module functions //// //// nextTick implementation with browser-compatible fallback //// @@ -97,42 +110,67 @@ async.each = function (arr, iterator, callback) { callback = callback || function () {}; - if (!arr.length) { + if (arr instanceof Array && !arr.length) { return callback(); } + var completed = 0; - _each(arr, function (x) { - iterator(x, only_once(function (err) { + var withKey = (iterator.length === 3); + + _each(arr, function (x, key) { + var args = []; + args.push(x); + if (withKey) + args.push(key); + args.push(only_once(function (err) { if (err) { callback(err); callback = function () {}; } else { completed += 1; - if (completed >= arr.length) { + if (completed >= _length(arr)) { callback(null); } } })); + + iterator.apply(iterator, args); }); }; async.forEach = async.each; async.eachSeries = function (arr, iterator, callback) { callback = callback || function () {}; - if (!arr.length) { + if (arr instanceof Array && !arr.length) { return callback(); } + if (!(arr instanceof Array)) { + var keys = _keys(arr); + if (!keys.length) + return callback(); + } + var completed = 0; + var withKey = (iterator.length === 3); + var iterate = function () { - iterator(arr[completed], function (err) { + var args = []; + var key = (arr instanceof Array) ? completed : keys[completed]; + var val = arr[key]; + + args.push(val); + if (withKey) { + args.push(key); + } + args.push(function (err) { if (err) { callback(err); callback = function () {}; } else { completed += 1; - if (completed >= arr.length) { + if (completed >= _length(arr)) { callback(null); } else { @@ -140,6 +178,7 @@ } } }); + iterator.apply(iterator, args); }; iterate(); }; diff --git a/test/test-async.js b/test/test-async.js index ff401e75b..8b72a1bfb 100755 --- a/test/test-async.js +++ b/test/test-async.js @@ -837,6 +837,32 @@ exports['each'] = function(test){ }); }; +exports['each with key'] = function(test){ + var count = 0; + var obj = {a: 1, b: 2, c: 3}; + async.each(obj, function(val, key, callback) { + count++; + test.equals(obj[key], val); + callback(); + if (count == 3) { + test.done(); + } + }); +}; + +exports['each with index'] = function(test){ + var count = 0; + var arr = ['a','b','c']; + async.each(arr, function(val, index, callback) { + count++; + test.equals(arr[index], val); + callback(); + if (count == 3) { + test.done(); + } + }); +}; + exports['each extra callback'] = function(test){ var count = 0; async.each([1,3,2], function(val, callback) {