From 710cc8cf5a094b664671f2003d51f28455f8a51b Mon Sep 17 00:00:00 2001 From: Olivier Lalonde Date: Tue, 28 May 2013 05:49:15 +0800 Subject: [PATCH 1/5] Implemented a way to pass a key argument to the iterator for async.each --- lib/async.js | 22 +++++++++++++++++++--- test/test-async.js | 26 ++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/lib/async.js b/lib/async.js index 46f4f509e..2cfb1f46c 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); } @@ -97,12 +104,19 @@ 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 () {}; @@ -114,6 +128,8 @@ } } })); + + iterator.apply(iterator, args); }); }; async.forEach = async.each; 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) { From df106f53c76f053ecc0330477e5c74d1d78cd9a2 Mon Sep 17 00:00:00 2001 From: Olivier Lalonde Date: Tue, 28 May 2013 06:00:50 +0800 Subject: [PATCH 2/5] Updated README.md with usage of async.each with a key --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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. From 180183def246545697ccdfdd883445d8f093a413 Mon Sep 17 00:00:00 2001 From: Olivier Lalonde Date: Tue, 28 May 2013 06:17:09 +0800 Subject: [PATCH 3/5] Fixed bug: cannot use arr.length with objects --- lib/async.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/async.js b/lib/async.js index 2cfb1f46c..01e612317 100755 --- a/lib/async.js +++ b/lib/async.js @@ -111,6 +111,18 @@ var completed = 0; var withKey = (iterator.length === 3); + function length (arr) { + if (arr instanceof Array) return arr.length; + if (Object.keys) return Object.keys(arr).length; + var count = 0; + for (var key in arr) { + if (Object.prototype.hasOwnProperty.call(arr, key)) { + count++; + } + } + return count; + } + _each(arr, function (x, key) { var args = []; args.push(x); @@ -123,7 +135,7 @@ } else { completed += 1; - if (completed >= arr.length) { + if (completed >= length(arr)) { callback(null); } } From ba4d138ba139df12419946b9693ddbbe4dbcb9af Mon Sep 17 00:00:00 2001 From: Olivier Lalonde Date: Tue, 28 May 2013 06:42:18 +0800 Subject: [PATCH 4/5] Implemented eachSeries with optional key argument --- lib/async.js | 42 ++++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/lib/async.js b/lib/async.js index 01e612317..ff2940905 100755 --- a/lib/async.js +++ b/lib/async.js @@ -77,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 //// @@ -111,18 +117,6 @@ var completed = 0; var withKey = (iterator.length === 3); - function length (arr) { - if (arr instanceof Array) return arr.length; - if (Object.keys) return Object.keys(arr).length; - var count = 0; - for (var key in arr) { - if (Object.prototype.hasOwnProperty.call(arr, key)) { - count++; - } - } - return count; - } - _each(arr, function (x, key) { var args = []; args.push(x); @@ -135,7 +129,7 @@ } else { completed += 1; - if (completed >= length(arr)) { + if (completed >= _length(arr)) { callback(null); } } @@ -148,19 +142,34 @@ async.eachSeries = function (arr, iterator, callback) { callback = callback || function () {}; - if (!arr.length) { + if (arr instanceof Array && !arr.length) { return callback(); } var completed = 0; + var withKey = (iterator.length === 3); + + if (!(arr instanceof Array)) { + var keys = _keys(arr); + } + console.log(keys); + 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 { @@ -168,6 +177,7 @@ } } }); + iterator.apply(iterator, args); }; iterate(); }; From 32db1825346e730907e3ff0dc860e3b7f02ae499 Mon Sep 17 00:00:00 2001 From: Olivier Lalonde Date: Tue, 28 May 2013 17:21:23 +0800 Subject: [PATCH 5/5] Removed console.log and fire callback immediately when object has no keys --- lib/async.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/async.js b/lib/async.js index ff2940905..0582db5cf 100755 --- a/lib/async.js +++ b/lib/async.js @@ -145,13 +145,14 @@ if (arr instanceof Array && !arr.length) { return callback(); } - var completed = 0; - var withKey = (iterator.length === 3); - if (!(arr instanceof Array)) { - var keys = _keys(arr); + var keys = _keys(arr); + if (!keys.length) + return callback(); } - console.log(keys); + + var completed = 0; + var withKey = (iterator.length === 3); var iterate = function () { var args = [];