From e0a51c362aad35fd6aad07c4dc463ba330deb634 Mon Sep 17 00:00:00 2001 From: Dominic Barnes Date: Fri, 10 Aug 2012 14:52:35 -0500 Subject: adding forEachOf to iterate objects asynchronously --- lib/async.js | 29 +++++++++++++++++++++++++++++ test/test-async.js | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/lib/async.js b/lib/async.js index 5f2fb18..179918e 100755 --- a/lib/async.js +++ b/lib/async.js @@ -57,6 +57,14 @@ return memo; }; + var _forEachOf = function (object, iterator) { + for (key in object) { + if (object.hasOwnProperty(key)) { + iterator(object[key], key); + } + } + }; + var _keys = function (obj) { if (Object.keys) { return Object.keys(obj); @@ -111,6 +119,27 @@ }); }; + async.forEachOf = function (object, iterator, callback) { + callback = callback || function () {}; + var completed = 0, size = _keys(object).length, key; + if (!size) { + return callback(); + } + _forEachOf(object, function (value, key) { + iterator(object[key], key, function (err) { + if (err) { + callback(err); + callback = function () {}; + } else { + completed += 1; + if (completed === size) { + callback(null); + } + } + }); + }); + }; + async.forEachSeries = function (arr, iterator, callback) { callback = callback || function () {}; if (!arr.length) { diff --git a/test/test-async.js b/test/test-async.js index 323527a..e26e936 100755 --- a/test/test-async.js +++ b/test/test-async.js @@ -17,6 +17,13 @@ function forEachIterator(args, x, callback) { }, x*25); } +function forEachOfIterator(args, x, key, callback) { + setTimeout(function(){ + args.push(key, x); + callback(); + }, x*25); +} + function mapIterator(call_order, x, callback) { setTimeout(function(){ call_order.push(x); @@ -43,6 +50,13 @@ function forEachNoCallbackIterator(test, x, callback) { test.done(); } +function forEachOfNoCallbackIterator(test, x, key, callback) { + test.equal(x, 1); + test.equal(key, "a"); + callback(); + test.done(); +} + function getFunctionsObject(call_order) { return { one: function(callback){ @@ -652,6 +666,39 @@ exports['forEach no callback'] = function(test){ async.forEach([1], forEachNoCallbackIterator.bind(this, test)); }; +exports['forEachOf'] = function(test){ + var args = []; + async.forEachOf({ a: 1, b: 2 }, forEachOfIterator.bind(this, args), function(err){ + test.same(args, ["a", 1, "b", 2]); + test.done(); + }); +}; + +exports['forEachOf empty object'] = function(test){ + test.expect(1); + async.forEachOf({}, function(value, key, callback){ + test.ok(false, 'iterator should not be called'); + callback(); + }, function(err) { + test.ok(true, 'should call callback'); + }); + setTimeout(test.done, 25); +}; + +exports['forEachOf error'] = function(test){ + test.expect(1); + async.forEachOf({ a: 1, b: 2 }, function(value, key, callback) { + callback('error'); + }, function(err){ + test.equals(err, 'error'); + }); + setTimeout(test.done, 50); +}; + +exports['forEachOf no callback'] = function(test){ + async.forEachOf({ a: 1 }, forEachOfNoCallbackIterator.bind(this, test)); +}; + exports['forEachSeries'] = function(test){ var args = []; async.forEachSeries([1,3,2], forEachIterator.bind(this, args), function(err){ -- cgit v1.2.1 From 2a13d0857682663e556b1a344d8c33d3a6c289bf Mon Sep 17 00:00:00 2001 From: Dominic Barnes Date: Wed, 6 Feb 2013 22:01:50 -0600 Subject: adding forEachOfSeries and forEachOfLimit along with tests --- lib/async.js | 88 ++++++++++++++++++++++++++++++- test/test-async.js | 151 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 235 insertions(+), 4 deletions(-) diff --git a/lib/async.js b/lib/async.js index 179918e..f1ef209 100755 --- a/lib/async.js +++ b/lib/async.js @@ -121,7 +121,8 @@ async.forEachOf = function (object, iterator, callback) { callback = callback || function () {}; - var completed = 0, size = _keys(object).length, key; + var size = object.length || _keys(object).length; + var completed = 0 if (!size) { return callback(); } @@ -173,6 +174,42 @@ iterate(); }; + async.forEachOfSeries = function (obj, iterator, callback) { + callback = callback || function () {}; + var keys = _keys(obj); + var size = keys.length; + if (!size) { + return callback(); + } + var completed = 0; + var iterate = function () { + var sync = true; + var key = keys[completed]; + iterator(obj[key], key, function (err) { + if (err) { + callback(err); + callback = function () {}; + } + else { + completed += 1; + if (completed >= size) { + callback(null); + } + else { + if (sync) { + async.nextTick(iterate); + } + else { + iterate(); + } + } + } + }); + sync = false; + }; + iterate(); + }; + async.forEachLimit = function (arr, limit, iterator, callback) { var fn = _forEachLimit(limit); fn.apply(null, [arr, iterator, callback]); @@ -219,6 +256,55 @@ }; + async.forEachOfLimit = function (obj, limit, iterator, callback) { + var fn = obj.constructor === Array ? _forEachOfLimit(limit) : _forEachOfLimit(limit); + fn.apply(null, [obj, iterator, callback]); + }; + + var _forEachOfLimit = function (limit) { + + return function (obj, iterator, callback) { + callback = callback || function () {}; + var keys = _keys(obj); + var size = keys.length; + if (!size || limit <= 0) { + return callback(); + } + var completed = 0; + var started = 0; + var running = 0; + + (function replenish () { + if (completed >= size) { + return callback(); + } + + while (running < limit && started < size) { + started += 1; + running += 1; + var key = keys[started - 1]; + iterator(obj[key], key, function (err) { + if (err) { + callback(err); + callback = function () {}; + } + else { + completed += 1; + running -= 1; + if (completed >= size) { + callback(); + } + else { + replenish(); + } + } + }); + } + })(); + }; + }; + + var doParallel = function (fn) { return function () { var args = Array.prototype.slice.call(arguments); diff --git a/test/test-async.js b/test/test-async.js index e26e936..cad8bb0 100755 --- a/test/test-async.js +++ b/test/test-async.js @@ -17,11 +17,11 @@ function forEachIterator(args, x, callback) { }, x*25); } -function forEachOfIterator(args, x, key, callback) { +function forEachOfIterator(args, value, key, callback) { setTimeout(function(){ - args.push(key, x); + args.push(key, value); callback(); - }, x*25); + }, value*25); } function mapIterator(call_order, x, callback) { @@ -699,6 +699,14 @@ exports['forEachOf no callback'] = function(test){ async.forEachOf({ a: 1 }, forEachOfNoCallbackIterator.bind(this, test)); }; +exports['forEachOf with array'] = function(test){ + var args = []; + async.forEachOf([ "a", "b" ], forEachOfIterator.bind(this, args), function(err){ + test.same(args, [0, "a", 1, "b"]); + test.done(); + }); +}; + exports['forEachSeries'] = function(test){ var args = []; async.forEachSeries([1,3,2], forEachIterator.bind(this, args), function(err){ @@ -735,6 +743,50 @@ exports['forEachSeries no callback'] = function(test){ async.forEachSeries([1], forEachNoCallbackIterator.bind(this, test)); }; +exports['forEachOfSeries'] = function(test){ + var args = []; + async.forEachOfSeries({ a: 1, b: 2 }, forEachOfIterator.bind(this, args), function(err){ + test.same(args, [ "a", 1, "b", 2 ]); + test.done(); + }); +}; + +exports['forEachOfSeries empty object'] = function(test){ + test.expect(1); + async.forEachOfSeries({}, function(x, callback){ + test.ok(false, 'iterator should not be called'); + callback(); + }, function(err){ + test.ok(true, 'should call callback'); + }); + setTimeout(test.done, 25); +}; + +exports['forEachOfSeries error'] = function(test){ + test.expect(2); + var call_order = []; + async.forEachOfSeries({ a: 1, b: 2 }, function(value, key, callback){ + call_order.push(value, key); + callback('error'); + }, function(err){ + test.same(call_order, [ 1, "a" ]); + test.equals(err, 'error'); + }); + setTimeout(test.done, 50); +}; + +exports['forEachOfSeries no callback'] = function(test){ + async.forEachOfSeries({ a: 1 }, forEachOfNoCallbackIterator.bind(this, test)); +}; + +exports['forEachOfSeries with array'] = function(test){ + var args = []; + async.forEachOfSeries([ "a", "b" ], forEachOfIterator.bind(this, args), function(err){ + test.same(args, [ 0, "a", 1, "b" ]); + test.done(); + }); +}; + exports['forEachLimit'] = function(test){ var args = []; var arr = [0,1,2,3,4,5,6,7,8,9]; @@ -822,6 +874,99 @@ exports['forEachLimit synchronous'] = function(test){ }); }; +exports['forEachOfLimit'] = function(test){ + var args = []; + var obj = { a: 1, b: 2, c: 3, d: 4 }; + async.forEachOfLimit(obj, 2, function(value, key, callback){ + setTimeout(function(){ + args.push(value, key); + callback(); + }, value * 5); + }, function(err){ + test.same(args, [ 1, "a", 2, "b", 3, "c", 4, "d" ]); + test.done(); + }); +}; + +exports['forEachOfLimit empty object'] = function(test){ + test.expect(1); + async.forEachOfLimit({}, 2, function(value, key, callback){ + test.ok(false, 'iterator should not be called'); + callback(); + }, function(err){ + test.ok(true, 'should call callback'); + }); + setTimeout(test.done, 25); +}; + +exports['forEachOfLimit limit exceeds size'] = function(test){ + var args = []; + var obj = { a: 1, b: 2, c: 3, d: 4, e: 5 }; + async.forEachOfLimit(obj, 10, forEachOfIterator.bind(this, args), function(err){ + test.same(args, [ "a", 1, "b", 2, "c", 3, "d", 4, "e", 5 ]); + test.done(); + }); +}; + +exports['forEachOfLimit limit equal size'] = function(test){ + var args = []; + var obj = { a: 1, b: 2, c: 3, d: 4, e: 5 }; + async.forEachOfLimit(obj, 5, forEachOfIterator.bind(this, args), function(err){ + test.same(args, [ "a", 1, "b", 2, "c", 3, "d", 4, "e", 5 ]); + test.done(); + }); +}; + +exports['forEachOfLimit zero limit'] = function(test){ + test.expect(1); + async.forEachOfLimit({ a: 1, b: 2 }, 0, function(x, callback){ + test.ok(false, 'iterator should not be called'); + callback(); + }, function(err){ + test.ok(true, 'should call callback'); + }); + setTimeout(test.done, 25); +}; + +exports['forEachOfLimit error'] = function(test){ + test.expect(2); + var obj = { a: 1, b: 2, c: 3, d: 4, e: 5 }; + var call_order = []; + + async.forEachOfLimit(obj, 3, function(value, key, callback){ + call_order.push(value, key); + if (value === 2) { + callback('error'); + } + }, function(err){ + test.same(call_order, [ 1, "a", 2, "b" ]); + test.equals(err, 'error'); + }); + setTimeout(test.done, 25); +}; + +exports['forEachOfLimit no callback'] = function(test){ + async.forEachOfLimit({ a: 1 }, 1, forEachOfNoCallbackIterator.bind(this, test)); +}; + +exports['forEachOfLimit synchronous'] = function(test){ + var args = []; + var obj = { a: 1, b: 2 }; + async.forEachOfLimit(obj, 5, forEachOfIterator.bind(this, args), function(err){ + test.same(args, [ "a", 1, "b", 2 ]); + test.done(); + }); +}; + +exports['forEachOfLimit with array'] = function(test){ + var args = []; + var arr = [ "a", "b" ] + async.forEachOfLimit(arr, 1, forEachOfIterator.bind(this, args), function (err) { + test.same(args, [ 0, "a", 1, "b" ]); + test.done(); + }); +}; + exports['map'] = function(test){ var call_order = []; async.map([1,3,2], mapIterator.bind(this, call_order), function(err, results){ -- cgit v1.2.1 From a2ac81ee875380a72e4f3cc3726f8523ff710404 Mon Sep 17 00:00:00 2001 From: Tom Boutell Date: Thu, 9 May 2013 12:28:16 -0400 Subject: Use of async.setImmediate prevents async.eachSeries from crashing the stack if the iterator function sometimes invokes the callback directly with 'return callback();' --- lib/async.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/async.js b/lib/async.js index 46f4f50..a3215f3 100755 --- a/lib/async.js +++ b/lib/async.js @@ -136,7 +136,9 @@ callback(null); } else { - iterate(); + async.setImmediate(function() { + iterate(); + }); } } }); -- cgit v1.2.1 From ec7a11f01e842f9241a27def203922d4a1b9841e Mon Sep 17 00:00:00 2001 From: Sean McCann Date: Wed, 28 May 2014 23:43:24 -0400 Subject: Correct spelling of 'An' --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8f4e98c..28cd3f7 100644 --- a/README.md +++ b/README.md @@ -1551,7 +1551,7 @@ by `memoize`. __Arguments__ * `fn` - The function to proxy and cache results from. -* `hasher` - Tn optional function for generating a custom hash for storing +* `hasher` - An optional function for generating a custom hash for storing results. It has all the arguments applied to it apart from the callback, and must be synchronous. -- cgit v1.2.1 From ac463aab272417fbd5bec89f1a713f17ef1be47e Mon Sep 17 00:00:00 2001 From: lion-man Date: Sat, 31 May 2014 21:31:47 +0900 Subject: Add jshintrc --- .jshintrc | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .jshintrc diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..3a2825a --- /dev/null +++ b/.jshintrc @@ -0,0 +1,19 @@ +{ + // Enforcing options + "eqeqeq": false, + "forin": true, + "indent": 4, + "noarg": true, + "undef": true, + "unused": true, + "trailing": true, + + // Relaxing options + "asi": false, + "eqnull": true, + "evil": true, + "expr": false, + "laxcomma": true, + "loopfunc": true, + "sub": true +} -- cgit v1.2.1 From 3a2b4481a6c0596f26c463427133fa6acf5e163c Mon Sep 17 00:00:00 2001 From: David Leonard Date: Sat, 16 Aug 2014 15:26:39 -0400 Subject: Updating download section --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8f4e98c..f900495 100644 --- a/README.md +++ b/README.md @@ -80,11 +80,15 @@ async.map([1, 2, 3], AsyncSquaringLibrary.square.bind(AsyncSquaringLibrary), fun ## Download The source is available for download from -[GitHub](http://github.com/caolan/async). +[GitHub](https://github.com/caolan/async/blob/master/lib/async.js). Alternatively, you can install using Node Package Manager (`npm`): npm install async +As well as using Bower: + + bower install async + __Development:__ [async.js](https://github.com/caolan/async/raw/master/lib/async.js) - 29.6kb Uncompressed ## In the Browser -- cgit v1.2.1 From 2f17fcb52de3575183f5899d8cf6f4f58b1ecfc9 Mon Sep 17 00:00:00 2001 From: Jeremy Brudvik Date: Fri, 5 Sep 2014 15:58:00 -0700 Subject: Add npm badge to README Add npm badge to README for easy access to npmjs.org link and npm version --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 8f4e98c..09f3c49 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Async.js [![Build Status via Travis CI](https://travis-ci.org/caolan/async.svg?branch=master)](https://travis-ci.org/caolan/async) +[![NPM version](http://img.shields.io/npm/v/async.svg)](https://www.npmjs.org/package/async) Async is a utility module which provides straight-forward, powerful functions -- cgit v1.2.1 From 0d2f15046f96e66707a6ba5c3c60a9a8df510e14 Mon Sep 17 00:00:00 2001 From: Vitaly Aminev Date: Thu, 18 Sep 2014 21:16:05 +0400 Subject: Update `applyEachSeries` function signature to match `applyEach` so it's not confusing what it does --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8f4e98c..4983906 100644 --- a/README.md +++ b/README.md @@ -1047,7 +1047,7 @@ async.each( --------------------------------------- -### applyEachSeries(arr, iterator, callback) +### applyEachSeries(arr, args..., callback) The same as [`applyEach`](#applyEach) only the functions are applied in series. -- cgit v1.2.1 From e4527c94362f6cbec0c965944a7b0462fd480f1b Mon Sep 17 00:00:00 2001 From: Tom Boutell Date: Mon, 22 Sep 2014 17:00:32 -0400 Subject: Document behavior of the default hash function of async.memoize --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 257def3..2f8c463 100644 --- a/README.md +++ b/README.md @@ -1313,6 +1313,8 @@ Caches the results of an async function. When creating a hash to store function results against, the callback is omitted from the hash and an optional hash function can be used. +If no hash function is specified, the first argument is used as a hash key, which may work reasonably if it is a string or a data type that converts to a distinct string. Note that objects and arrays will not behave reasonably. Neither will cases where the other arguments are significant. In such cases, specify your own hash function. + The cache of results is exposed as the `memo` property of the function returned by `memoize`. -- cgit v1.2.1 From cdddd3491461e479ab4f70c8f99df216b52f100d Mon Sep 17 00:00:00 2001 From: Tom Boutell Date: Mon, 22 Sep 2014 17:02:45 -0400 Subject: Back out an earlier rejected pull request --- lib/async.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/async.js b/lib/async.js index a3215f3..46f4f50 100755 --- a/lib/async.js +++ b/lib/async.js @@ -136,9 +136,7 @@ callback(null); } else { - async.setImmediate(function() { - iterate(); - }); + iterate(); } } }); -- cgit v1.2.1 From 2410077e26d5429ac45533438f008706dcb989f7 Mon Sep 17 00:00:00 2001 From: Mickael van der Beek Date: Tue, 18 Nov 2014 21:20:56 +0100 Subject: Fixes caolan/async#263 and other dependency issues by detecting dead-locks and throwing an Error. --- lib/async.js | 11 +++++++++++ test/test-async.js | 27 +++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/lib/async.js b/lib/async.js index a13f835..50f7a28 100755 --- a/lib/async.js +++ b/lib/async.js @@ -478,6 +478,17 @@ } }; var requires = task.slice(0, Math.abs(task.length - 1)) || []; + // prevent dead-locks + var len = requires.length; + var dep; + while (len--) { + if (!(dep = tasks[requires[len]])) { + throw new Error('Has inexistant dependency'); + } + if (_isArray(dep) && !!~dep.indexOf(k)) { + throw new Error('Has cyclic dependencies'); + } + } var ready = function () { return _reduce(requires, function (a, x) { return (a && results.hasOwnProperty(x)); diff --git a/test/test-async.js b/test/test-async.js index 6a35606..8ee2bd9 100755 --- a/test/test-async.js +++ b/test/test-async.js @@ -580,6 +580,33 @@ exports['auto modifying results causes final callback to run early'] = function( }); }; +// Issue 263 on github: https://github.com/caolan/async/issues/263 +exports['auto prevent dead-locks due to inexistant dependencies'] = function(test) { + test.throws(function () { + async.auto({ + task1: ['noexist', function(callback, results){ + callback(null, 'task1'); + }] + }); + }, Error); + test.done(); +}; + +// Issue 263 on github: https://github.com/caolan/async/issues/263 +exports['auto prevent dead-locks due to cyclic dependencies'] = function(test) { + test.throws(function () { + async.auto({ + task3: ['task4', function(callback, results){ + callback(null, 'task3'); + }], + task4: ['task3', function(callback, results){ + callback(null, 'task4'); + }] + }); + }, Error); + test.done(); +}; + // Issue 306 on github: https://github.com/caolan/async/issues/306 exports['retry when attempt succeeds'] = function(test) { var failed = 3 -- cgit v1.2.1 From 74819d24051d1457c73ab301d697ec8b467115f8 Mon Sep 17 00:00:00 2001 From: Mickael van der Beek Date: Tue, 18 Nov 2014 21:24:02 +0100 Subject: Corrected task name typo for consistency with other tests. --- test/test-async.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/test-async.js b/test/test-async.js index 8ee2bd9..a84cb49 100755 --- a/test/test-async.js +++ b/test/test-async.js @@ -596,11 +596,11 @@ exports['auto prevent dead-locks due to inexistant dependencies'] = function(tes exports['auto prevent dead-locks due to cyclic dependencies'] = function(test) { test.throws(function () { async.auto({ - task3: ['task4', function(callback, results){ - callback(null, 'task3'); + task1: ['task2', function(callback, results){ + callback(null, 'task1'); }], - task4: ['task3', function(callback, results){ - callback(null, 'task4'); + task2: ['task1', function(callback, results){ + callback(null, 'task2'); }] }); }, Error); -- cgit v1.2.1 From 0789c3185a0d67dca06176d2b4e4aac9373cac12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B0=D0=BB=D1=8C=D0=BE?= Date: Tue, 30 Dec 2014 16:41:16 +0200 Subject: Update README.md Update times signature. Tried to be more exact and not confusing the map's callback with this "callback" --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5123e97..efa80cb 100644 --- a/README.md +++ b/README.md @@ -1508,7 +1508,8 @@ you would use with [`map`](#map). __Arguments__ * `n` - The number of times to run the function. -* `callback` - The function to call `n` times. +* `iterator` - The function to call `n` times. +* `callback` - see [`map`](#map) __Example__ -- cgit v1.2.1 From 6d612c8bceff7a88ebcc263a139f336adec79e9e Mon Sep 17 00:00:00 2001 From: Alexander Early Date: Mon, 19 Jan 2015 23:53:49 -0800 Subject: adding docs for forEachOf methods --- README.md | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/README.md b/README.md index 420cd6a..0f2e41b 100644 --- a/README.md +++ b/README.md @@ -119,6 +119,9 @@ Usage: * [`each`](#each) * [`eachSeries`](#eachSeries) * [`eachLimit`](#eachLimit) +* [`forEachOf`](#forEachOf) +* [`forEachOfSeries`](#forEachOfSeries) +* [`forEachOfLimit`](#forEachOfLimit) * [`map`](#map) * [`mapSeries`](#mapSeries) * [`mapLimit`](#mapLimit) @@ -280,6 +283,53 @@ async.eachLimit(documents, 20, requestApi, function(err){ }); ``` +--------------------------------------- + + + +### forEachOf(obj, iterator, callback) + +Like `each`, except that it iterates over objects, and passes the key as the second argument to the iterator. + +__Arguments__ + +* `obj` - An object or array to iterate over. +* `iterator(item, key, callback)` - A function to apply to each item in `obj`. +The `key` is the item's key, or index in the case of an array. 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 when all `iterator` functions have finished, or an error occurs. + +__Example__ + +```js +var obj = {a: 1, b: 2, c: 3}; + +async.forEachOf(obj, function (value, key, callback) { + console.log(value + ":" + key) // 1:a, 2:b, 3:c, etc... + callback(); +}, function (err) { + +}) +``` + +--------------------------------------- + + + +### forEachOfSeries(obj, iterator, callback) + +Like [`forEachOf`](#forEachOf), except only one `iterator` is run at a time. The order of execution is not guaranteed for objects, but it will be for arrays. +--------------------------------------- + + + +### forEachOfLimit(obj, limit, iterator, callback) + +Like [`forEachOf`](#forEachOf), except only one the number of `iterator`s running at a time is controlled by `limit`. The order of execution is not guaranteed for objects, but it will be for arrays. + + --------------------------------------- -- cgit v1.2.1 From 52dd10d744a3c2d82b3f29466462e569adc8de39 Mon Sep 17 00:00:00 2001 From: Alexander Early Date: Tue, 20 Jan 2015 11:18:50 -0800 Subject: reordered functions, added aliases, simplified eachOfLimit logic --- lib/async.js | 104 +++++++++++++++++++++++++++-------------------------- test/test-async.js | 97 ++++++++++++++++++++++++------------------------- 2 files changed, 102 insertions(+), 99 deletions(-) diff --git a/lib/async.js b/lib/async.js index c99024b..185812f 100644 --- a/lib/async.js +++ b/lib/async.js @@ -171,7 +171,56 @@ }; async.forEachSeries = async.eachSeries; - async.forEachOf = function (object, iterator, callback) { + + async.eachLimit = function (arr, limit, iterator, callback) { + var fn = _eachLimit(limit); + fn.apply(null, [arr, iterator, callback]); + }; + async.forEachLimit = async.eachLimit; + + var _eachLimit = function (limit) { + + return function (arr, iterator, callback) { + callback = callback || function () {}; + if (!arr.length || limit <= 0) { + return callback(); + } + var completed = 0; + var started = 0; + var running = 0; + + (function replenish () { + if (completed >= arr.length) { + return callback(); + } + + while (running < limit && started < arr.length) { + started += 1; + running += 1; + iterator(arr[started - 1], function (err) { + if (err) { + callback(err); + callback = function () {}; + } + else { + completed += 1; + running -= 1; + if (completed >= arr.length) { + callback(); + } + else { + replenish(); + } + } + }); + } + })(); + }; + }; + + + + async.forEachOf = async.eachOf = function (object, iterator, callback) { callback = callback || function () {}; var size = object.length || _keys(object).length; var completed = 0 @@ -193,7 +242,7 @@ }); }; - async.forEachOfSeries = function (obj, iterator, callback) { + async.forEachOfSeries = async.eachOfSeries = function (obj, iterator, callback) { callback = callback || function () {}; var keys = _keys(obj); var size = keys.length; @@ -230,56 +279,9 @@ }; - async.eachLimit = function (arr, limit, iterator, callback) { - var fn = _eachLimit(limit); - fn.apply(null, [arr, iterator, callback]); - }; - async.forEachLimit = async.eachLimit; - - var _eachLimit = function (limit) { - - return function (arr, iterator, callback) { - callback = callback || function () {}; - if (!arr.length || limit <= 0) { - return callback(); - } - var completed = 0; - var started = 0; - var running = 0; - - (function replenish () { - if (completed >= arr.length) { - return callback(); - } - - while (running < limit && started < arr.length) { - started += 1; - running += 1; - iterator(arr[started - 1], function (err) { - if (err) { - callback(err); - callback = function () {}; - } - else { - completed += 1; - running -= 1; - if (completed >= arr.length) { - callback(); - } - else { - replenish(); - } - } - }); - } - })(); - }; - }; - - async.forEachOfLimit = function (obj, limit, iterator, callback) { - var fn = obj.constructor === Array ? _forEachOfLimit(limit) : _forEachOfLimit(limit); - fn.apply(null, [obj, iterator, callback]); + async.forEachOfLimit = async.eachOfLimit = function (obj, limit, iterator, callback) { + _forEachOfLimit(limit)(obj, iterator, callback); }; var _forEachOfLimit = function (limit) { diff --git a/test/test-async.js b/test/test-async.js index f5fc72e..73e7122 100755 --- a/test/test-async.js +++ b/test/test-async.js @@ -1256,54 +1256,6 @@ exports['eachSeries no callback'] = function(test){ async.eachSeries([1], eachNoCallbackIterator.bind(this, test)); }; -exports['forEachSeries alias'] = function (test) { - test.strictEqual(async.eachSeries, async.forEachSeries); - test.done(); -}; - -exports['forEachOfSeries'] = function(test){ - var args = []; - async.forEachOfSeries({ a: 1, b: 2 }, forEachOfIterator.bind(this, args), function(err){ - test.same(args, [ "a", 1, "b", 2 ]); - test.done(); - }); -}; - -exports['forEachOfSeries empty object'] = function(test){ - test.expect(1); - async.forEachOfSeries({}, function(x, callback){ - test.ok(false, 'iterator should not be called'); - callback(); - }, function(err){ - test.ok(true, 'should call callback'); - }); - setTimeout(test.done, 25); -}; - -exports['forEachOfSeries error'] = function(test){ - test.expect(2); - var call_order = []; - async.forEachOfSeries({ a: 1, b: 2 }, function(value, key, callback){ - call_order.push(value, key); - callback('error'); - }, function(err){ - test.same(call_order, [ 1, "a" ]); - test.equals(err, 'error'); - }); - setTimeout(test.done, 50); -}; - -exports['forEachOfSeries no callback'] = function(test){ - async.forEachOfSeries({ a: 1 }, forEachOfNoCallbackIterator.bind(this, test)); -}; - -exports['forEachOfSeries with array'] = function(test){ - var args = []; - async.forEachOfSeries([ "a", "b" ], forEachOfIterator.bind(this, args), function(err){ - test.same(args, [ 0, "a", 1, "b" ]); - test.done(); - }); -}; exports['eachLimit'] = function(test){ var args = []; @@ -1392,6 +1344,55 @@ exports['eachLimit synchronous'] = function(test){ }); }; +exports['forEachSeries alias'] = function (test) { + test.strictEqual(async.eachSeries, async.forEachSeries); + test.done(); +}; + +exports['forEachOfSeries'] = function(test){ + var args = []; + async.forEachOfSeries({ a: 1, b: 2 }, forEachOfIterator.bind(this, args), function(err){ + test.same(args, [ "a", 1, "b", 2 ]); + test.done(); + }); +}; + +exports['forEachOfSeries empty object'] = function(test){ + test.expect(1); + async.forEachOfSeries({}, function(x, callback){ + test.ok(false, 'iterator should not be called'); + callback(); + }, function(err){ + test.ok(true, 'should call callback'); + }); + setTimeout(test.done, 25); +}; + +exports['forEachOfSeries error'] = function(test){ + test.expect(2); + var call_order = []; + async.forEachOfSeries({ a: 1, b: 2 }, function(value, key, callback){ + call_order.push(value, key); + callback('error'); + }, function(err){ + test.same(call_order, [ 1, "a" ]); + test.equals(err, 'error'); + }); + setTimeout(test.done, 50); +}; + +exports['forEachOfSeries no callback'] = function(test){ + async.forEachOfSeries({ a: 1 }, forEachOfNoCallbackIterator.bind(this, test)); +}; + +exports['forEachOfSeries with array'] = function(test){ + var args = []; + async.forEachOfSeries([ "a", "b" ], forEachOfIterator.bind(this, args), function(err){ + test.same(args, [ 0, "a", 1, "b" ]); + test.done(); + }); +}; + exports['forEachLimit alias'] = function (test) { test.strictEqual(async.eachLimit, async.forEachLimit); test.done(); -- cgit v1.2.1 From ae5d25f9e139af86bc5ef1431380e6f20f8eeea9 Mon Sep 17 00:00:00 2001 From: Alexander Early Date: Tue, 20 Jan 2015 11:30:12 -0800 Subject: tightening up the documentation --- README.md | 29 ++++++++++++++++++++++------- lib/async.js | 2 +- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 0f2e41b..887e386 100644 --- a/README.md +++ b/README.md @@ -194,7 +194,8 @@ __Arguments__ * `iterator(item, callback)` - A function to apply to each item in `arr`. 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. + arguments or with an explicit `null` argument. The array index is not passed + to the iterator. If you need the index, use [`forEachOf`](#forEachOf). * `callback(err)` - A callback which is called when all `iterator` functions have finished, or an error occurs. @@ -286,6 +287,7 @@ async.eachLimit(documents, 20, requestApi, function(err){ --------------------------------------- + ### forEachOf(obj, iterator, callback) @@ -304,30 +306,43 @@ explicit `null` argument. __Example__ ```js -var obj = {a: 1, b: 2, c: 3}; +var obj = {dev: "/dev.json", test: "/test.json", prod: "/prod.json"}; +var configs = {}; async.forEachOf(obj, function (value, key, callback) { - console.log(value + ":" + key) // 1:a, 2:b, 3:c, etc... - callback(); + fs.readFile(__dirname + value, "utf8", function (err, data) { + if (err) return callback(err); + try { + configs[key] = JSON.parse(data); + } catch (e) { + return callback(e); + } + callback(); + }) }, function (err) { - + if (err) console.error(err.message); + // configs is now a map of JSON data + doSomethingWith(configs); }) ``` --------------------------------------- + ### forEachOfSeries(obj, iterator, callback) -Like [`forEachOf`](#forEachOf), except only one `iterator` is run at a time. The order of execution is not guaranteed for objects, but it will be for arrays. +Like [`forEachOf`](#forEachOf), except only one `iterator` is run at a time. The order of execution is not guaranteed for objects, but it will be guaranteed for arrays. + --------------------------------------- + ### forEachOfLimit(obj, limit, iterator, callback) -Like [`forEachOf`](#forEachOf), except only one the number of `iterator`s running at a time is controlled by `limit`. The order of execution is not guaranteed for objects, but it will be for arrays. +Like [`forEachOf`](#forEachOf), except the number of `iterator`s running at a given time is controlled by `limit`. --------------------------------------- diff --git a/lib/async.js b/lib/async.js index 185812f..87ebe47 100644 --- a/lib/async.js +++ b/lib/async.js @@ -280,7 +280,7 @@ - async.forEachOfLimit = async.eachOfLimit = function (obj, limit, iterator, callback) { + async.forEachOfLimit = async.eachOfLimit = function (obj, limit, iterator, callback) { _forEachOfLimit(limit)(obj, iterator, callback); }; -- cgit v1.2.1 From d00bfc5655d17117900ed17704b1ce0295ce6f2b Mon Sep 17 00:00:00 2001 From: Neamar Date: Mon, 9 Jun 2014 19:56:27 +0200 Subject: noop --- lib/async.js | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/lib/async.js b/lib/async.js index 394c41c..8aec08f 100644 --- a/lib/async.js +++ b/lib/async.js @@ -10,6 +10,7 @@ (function () { var async = {}; + var noop = function () {}; // global on the server, window in the browser var root, previous_async; @@ -113,7 +114,7 @@ } async.each = function (arr, iterator, callback) { - callback = callback || function () {}; + callback = callback || noop; if (!arr.length) { return callback(); } @@ -124,7 +125,7 @@ function done(err) { if (err) { callback(err); - callback = function () {}; + callback = noop; } else { completed += 1; @@ -137,7 +138,7 @@ async.forEach = async.each; async.eachSeries = function (arr, iterator, callback) { - callback = callback || function () {}; + callback = callback || noop; if (!arr.length) { return callback(); } @@ -146,7 +147,7 @@ iterator(arr[completed], function (err) { if (err) { callback(err); - callback = function () {}; + callback = noop; } else { completed += 1; @@ -172,7 +173,7 @@ var _eachLimit = function (limit) { return function (arr, iterator, callback) { - callback = callback || function () {}; + callback = callback || noop; if (!arr.length || limit <= 0) { return callback(); } @@ -191,7 +192,7 @@ iterator(arr[started - 1], function (err) { if (err) { callback(err); - callback = function () {}; + callback = noop; } else { completed += 1; @@ -342,7 +343,7 @@ iterator(x, function (result) { if (result) { main_callback(x); - main_callback = function () {}; + main_callback = noop; } else { callback(); @@ -360,7 +361,7 @@ iterator(x, function (v) { if (v) { main_callback(true); - main_callback = function () {}; + main_callback = noop; } callback(); }); @@ -376,7 +377,7 @@ iterator(x, function (v) { if (!v) { main_callback(false); - main_callback = function () {}; + main_callback = noop; } callback(); }); @@ -414,7 +415,7 @@ }; async.auto = function (tasks, callback) { - callback = callback || function () {}; + callback = callback || noop; var keys = _keys(tasks); var remainingTasks = keys.length if (!remainingTasks) { @@ -446,7 +447,7 @@ if (!remainingTasks) { var theCallback = callback; // prevent final callback from calling itself if it errors - callback = function () {}; + callback = noop; theCallback(null, results); } @@ -467,7 +468,7 @@ safeResults[k] = args; callback(err, safeResults); // stop subsequent errors hitting callback multiple times - callback = function () {}; + callback = noop; } else { results[k] = args; @@ -527,7 +528,7 @@ }; async.waterfall = function (tasks, callback) { - callback = callback || function () {}; + callback = callback || noop; if (!_isArray(tasks)) { var err = new Error('First argument to waterfall must be an array of functions'); return callback(err); @@ -539,7 +540,7 @@ return function (err) { if (err) { callback.apply(null, arguments); - callback = function () {}; + callback = noop; } else { var args = Array.prototype.slice.call(arguments, 1); @@ -560,7 +561,7 @@ }; var _parallel = function(eachfn, tasks, callback) { - callback = callback || function () {}; + callback = callback || noop; if (_isArray(tasks)) { eachfn.map(tasks, function (fn, callback) { if (fn) { @@ -600,7 +601,7 @@ }; async.series = function (tasks, callback) { - callback = callback || function () {}; + callback = callback || noop; if (_isArray(tasks)) { async.mapSeries(tasks, function (fn, callback) { if (fn) { -- cgit v1.2.1 From ddcee3e0ad11771360a726c460a3427c9c78a566 Mon Sep 17 00:00:00 2001 From: Joseph Orbegoso Pea Date: Wed, 7 May 2014 12:37:56 -0700 Subject: Better support for browsers. In a RequireJS environment `this` doesn't refer to the `window` object, so async doesn't work there. --- lib/async.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/async.js b/lib/async.js index 394c41c..f925fc5 100644 --- a/lib/async.js +++ b/lib/async.js @@ -14,7 +14,16 @@ // global on the server, window in the browser var root, previous_async; - root = this; + if (typeof window == 'object' && this === window) { + root = window; + } + else if (typeof global == 'object' && this === global) { + root = global; + } + else { + root = this; + } + if (root != null) { previous_async = root.async; } -- cgit v1.2.1 From d0149c9978c5903254f1ee115e653d7026fa4c76 Mon Sep 17 00:00:00 2001 From: Elvin Yung Date: Fri, 27 Feb 2015 14:48:37 -0500 Subject: Wrap makefile cwd strings in quotes --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index bad647c..4b0f23c 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ PACKAGE = asyncjs NODEJS = $(if $(shell test -f /usr/bin/nodejs && echo "true"),nodejs,node) -CWD := $(shell pwd) +CWD := "$(shell pwd)" NODEUNIT = $(CWD)/node_modules/nodeunit/bin/nodeunit UGLIFY = $(CWD)/node_modules/uglify-js/bin/uglifyjs NODELINT = $(CWD)/node_modules/nodelint/nodelint -- cgit v1.2.1 From e7ab7d73d1e81a6965343db09f1e297af9360811 Mon Sep 17 00:00:00 2001 From: Gregor MacLennan Date: Sun, 29 Mar 2015 19:41:40 -0700 Subject: Add cargo animation to README For some reason the README only included one of @rhyzx 's brilliant animations, which really help explain `cargo` --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6cfb922..88ecd73 100644 --- a/README.md +++ b/README.md @@ -1154,7 +1154,7 @@ Creates a `cargo` object with the specified payload. Tasks added to the cargo will be processed altogether (up to the `payload` limit). If the `worker` is in progress, the task is queued until it becomes available. Once the `worker` has completed some tasks, each callback of those tasks is called. -Check out [this animation](https://camo.githubusercontent.com/6bbd36f4cf5b35a0f11a96dcd2e97711ffc2fb37/68747470733a2f2f662e636c6f75642e6769746875622e636f6d2f6173736574732f313637363837312f36383130382f62626330636662302d356632392d313165322d393734662d3333393763363464633835382e676966) for how `cargo` and `queue` work. +Check out [these](https://camo.githubusercontent.com/6bbd36f4cf5b35a0f11a96dcd2e97711ffc2fb37/68747470733a2f2f662e636c6f75642e6769746875622e636f6d2f6173736574732f313637363837312f36383130382f62626330636662302d356632392d313165322d393734662d3333393763363464633835382e676966) [animations](https://camo.githubusercontent.com/f4810e00e1c5f5f8addbe3e9f49064fd5d102699/68747470733a2f2f662e636c6f75642e6769746875622e636f6d2f6173736574732f313637363837312f36383130312f38346339323036362d356632392d313165322d383134662d3964336430323431336266642e676966) for how `cargo` and `queue` work. While [queue](#queue) passes only one task to one of a group of workers at a time, cargo passes an array of tasks to a single worker, repeating -- cgit v1.2.1 From 88507339f6ac7094cb06653a015503221da510f9 Mon Sep 17 00:00:00 2001 From: Beau Gunderson Date: Mon, 18 May 2015 19:26:55 -0700 Subject: Use regular license string --- package.json | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index f5debe2..fb329d9 100644 --- a/package.json +++ b/package.json @@ -17,10 +17,7 @@ "bugs": { "url": "https://github.com/caolan/async/issues" }, - "license": { - "type": "MIT", - "url": "https://github.com/caolan/async/raw/master/LICENSE" - }, + "license": "MIT", "devDependencies": { "nodeunit": ">0.0.0", "uglify-js": "1.2.x", @@ -54,4 +51,4 @@ "tests" ] } -} \ No newline at end of file +} -- cgit v1.2.1 From a034c1af6667fc2987325cf5be8ba2577986d27a Mon Sep 17 00:00:00 2001 From: Beau Gunderson Date: Mon, 18 May 2015 19:34:25 -0700 Subject: Update package manager sync script --- bower.json | 2 +- package.json | 2 +- support/sync-package-managers.js | 9 +++------ 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/bower.json b/bower.json index e6f1b42..1817688 100644 --- a/bower.json +++ b/bower.json @@ -9,6 +9,7 @@ "utility", "module" ], + "license": "MIT", "repository": { "type": "git", "url": "https://github.com/caolan/async.git" @@ -24,7 +25,6 @@ "globals", "node" ], - "license": "MIT", "ignore": [ "**/.*", "node_modules", diff --git a/package.json b/package.json index fb329d9..1424c76 100644 --- a/package.json +++ b/package.json @@ -51,4 +51,4 @@ "tests" ] } -} +} \ No newline at end of file diff --git a/support/sync-package-managers.js b/support/sync-package-managers.js index 310bbe6..30cb7c2 100755 --- a/support/sync-package-managers.js +++ b/support/sync-package-managers.js @@ -13,8 +13,6 @@ var IGNORES = ['**/.*', 'node_modules', 'bower_components', 'test', 'tests']; var INCLUDES = ['lib/async.js', 'README.md', 'LICENSE']; var REPOSITORY_NAME = 'caolan/async'; -var LICENSE_NAME = packageJson.license.type; - packageJson.jam = { main: packageJson.main, include: INCLUDES, @@ -32,21 +30,20 @@ packageJson.volo = { var bowerSpecific = { moduleType: ['amd', 'globals', 'node'], - license: LICENSE_NAME, ignore: IGNORES, authors: [packageJson.author] }; var bowerInclude = ['name', 'description', 'version', 'main', 'keywords', - 'homepage', 'repository', 'devDependencies']; + 'license', 'homepage', 'repository', 'devDependencies']; var componentSpecific = { - license: LICENSE_NAME, repository: REPOSITORY_NAME, scripts: [packageJson.main] }; -var componentInclude = ['name', 'description', 'version', 'keywords']; +var componentInclude = ['name', 'description', 'version', 'keywords', + 'license']; var bowerJson = _.merge({}, _.pick(packageJson, bowerInclude), bowerSpecific); var componentJson = _.merge({}, _.pick(packageJson, componentInclude), componentSpecific); -- cgit v1.2.1 From de3a16091d5125384eff4a54deb3998b13c3814c Mon Sep 17 00:00:00 2001 From: Beau Gunderson Date: Mon, 18 May 2015 19:59:38 -0700 Subject: =?UTF-8?q?0.11=20=E2=86=92=200.12,=20add=20iojs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 05d299e..6064ca0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,5 @@ language: node_js node_js: - "0.10" - - "0.11" + - "0.12" + - "iojs" -- cgit v1.2.1 From 074e091ec0a02be00627437c7a467984dcf03e91 Mon Sep 17 00:00:00 2001 From: Alexander Early Date: Tue, 19 May 2015 17:37:48 -0700 Subject: fixed jshint issues, run linter on npm test --- .jshintrc | 13 ++++--- lib/async.js | 30 ++++++++-------- package.json | 12 ++++--- test/test-async.js | 101 ++++++++++++++++++++++++++--------------------------- 4 files changed, 79 insertions(+), 77 deletions(-) diff --git a/.jshintrc b/.jshintrc index 3a2825a..172f491 100644 --- a/.jshintrc +++ b/.jshintrc @@ -5,15 +5,20 @@ "indent": 4, "noarg": true, "undef": true, - "unused": true, "trailing": true, + "evil": true, + "laxcomma": true, // Relaxing options + "onevar": false, "asi": false, "eqnull": true, - "evil": true, "expr": false, - "laxcomma": true, "loopfunc": true, - "sub": true + "sub": true, + "browser": true, + "node": true, + "globals": { + "define": true + } } diff --git a/lib/async.js b/lib/async.js index 108bc9d..d6cd961 100644 --- a/lib/async.js +++ b/lib/async.js @@ -5,8 +5,6 @@ * Copyright 2010-2014 Caolan McMahon * Released under the MIT license */ -/*jshint onevar: false, indent:4 */ -/*global setImmediate: false, setTimeout: false, console: false */ (function () { var async = {}; @@ -40,7 +38,7 @@ if (called) throw new Error("Callback was already called."); called = true; fn.apply(root, arguments); - } + }; } //// cross-browser compatiblity functions //// @@ -79,7 +77,7 @@ }; var _forEachOf = function (object, iterator) { - for (key in object) { + for (var key in object) { if (object.hasOwnProperty(key)) { iterator(object[key], key); } @@ -233,7 +231,7 @@ async.forEachOf = async.eachOf = function (object, iterator, callback) { callback = callback || function () {}; var size = object.length || _keys(object).length; - var completed = 0 + var completed = 0; if (!size) { return callback(); } @@ -544,7 +542,7 @@ async.auto = function (tasks, callback) { callback = callback || noop; var keys = _keys(tasks); - var remainingTasks = keys.length + var remainingTasks = keys.length; if (!remainingTasks) { return callback(); } @@ -564,7 +562,7 @@ } }; var taskComplete = function () { - remainingTasks-- + remainingTasks--; _each(listeners.slice(0), function (fn) { fn(); }); @@ -660,9 +658,9 @@ data = data[data.length - 1]; (wrappedCallback || callback)(data.err, data.result); }); - } + }; // If a callback is passed, run this as a controll flow - return callback ? wrappedTask() : wrappedTask + return callback ? wrappedTask() : wrappedTask; }; async.waterfall = function (tasks, callback) { @@ -878,7 +876,7 @@ if (!_isArray(data)) { data = [data]; } - if(data.length == 0) { + if(data.length === 0) { // call drain immediately if there are no tasks return async.setImmediate(function() { if (q.drain) { @@ -975,7 +973,7 @@ function _compareTasks(a, b){ return a.priority - b.priority; - }; + } function _binarySearch(sequence, item, compare) { var beg = -1, @@ -998,7 +996,7 @@ if (!_isArray(data)) { data = [data]; } - if(data.length == 0) { + if(data.length === 0) { // call drain immediately if there are no tasks return async.setImmediate(function() { if (q.drain) { @@ -1071,9 +1069,9 @@ return; } - var ts = typeof payload === 'number' - ? tasks.splice(0, payload) - : tasks.splice(0, tasks.length); + var ts = typeof payload === 'number' ? + tasks.splice(0, payload) : + tasks.splice(0, tasks.length); var ds = _map(ts, function (task) { return task.data; @@ -1198,7 +1196,7 @@ var err = arguments[0]; var nextargs = Array.prototype.slice.call(arguments, 1); cb(err, nextargs); - }])) + }])); }, function (err, results) { callback.apply(that, [err].concat(results)); diff --git a/package.json b/package.json index 1424c76..ffb6491 100644 --- a/package.json +++ b/package.json @@ -19,10 +19,11 @@ }, "license": "MIT", "devDependencies": { - "nodeunit": ">0.0.0", - "uglify-js": "1.2.x", + "jshint": "~2.7.0", + "lodash": ">=2.4.1", "nodelint": ">0.0.0", - "lodash": ">=2.4.1" + "nodeunit": ">0.0.0", + "uglify-js": "1.2.x" }, "jam": { "main": "lib/async.js", @@ -36,7 +37,8 @@ ] }, "scripts": { - "test": "nodeunit test/test-async.js" + "test": "npm run-script lint && nodeunit test/test-async.js", + "lint": "jshint lib/async.js test/test-async.js" }, "spm": { "main": "lib/async.js" @@ -51,4 +53,4 @@ "tests" ] } -} \ No newline at end of file +} diff --git a/test/test-async.js b/test/test-async.js index e660c1b..6f6a741 100755 --- a/test/test-async.js +++ b/test/test-async.js @@ -6,7 +6,7 @@ if (!Function.prototype.bind) { var self = this; return function () { self.apply(thisArg, args.concat(Array.prototype.slice.call(arguments))); - } + }; }; } @@ -526,7 +526,7 @@ exports['auto error should pass partial results'] = function(test) { // Issue 76 on github: https://github.com/caolan/async/issues#issue/76 exports['auto removeListener has side effect on loop iterator'] = function(test) { async.auto({ - task1: ['task3', function(callback) { test.done() }], + task1: ['task3', function(callback) { test.done(); }], task2: ['task3', function(callback) { /* by design: DON'T call callback */ }], task3: function(callback) { callback(); } }); @@ -573,7 +573,7 @@ exports['auto calls callback multiple times'] = function(test) { exports['auto modifying results causes final callback to run early'] = function(test) { async.auto({ task1: function(callback, results){ - results.inserted = true + results.inserted = true; callback(null, 'task1'); }, task2: function(callback){ @@ -588,8 +588,8 @@ exports['auto modifying results causes final callback to run early'] = function( } }, function(err, results){ - test.equal(results.inserted, true) - test.ok(results.task3, 'task3') + test.equal(results.inserted, true); + test.ok(results.task3, 'task3'); test.done(); }); }; @@ -623,18 +623,18 @@ exports['auto prevent dead-locks due to cyclic dependencies'] = function(test) { // Issue 306 on github: https://github.com/caolan/async/issues/306 exports['retry when attempt succeeds'] = function(test) { - var failed = 3 - var callCount = 0 - var expectedResult = 'success' + var failed = 3; + var callCount = 0; + var expectedResult = 'success'; function fn(callback, results) { - callCount++ - failed-- - if (!failed) callback(null, expectedResult) - else callback(true) // respond with error + callCount++; + failed--; + if (!failed) callback(null, expectedResult); + else callback(true); // respond with error } async.retry(fn, function(err, result){ - test.equal(callCount, 3, 'did not retry the correct number of times') - test.equal(result, expectedResult, 'did not return the expected result') + test.equal(callCount, 3, 'did not retry the correct number of times'); + test.equal(result, expectedResult, 'did not return the expected result'); test.done(); }); }; @@ -647,7 +647,7 @@ exports['retry when all attempts succeeds'] = function(test) { function fn(callback, results) { callCount++; callback(error + callCount, erroredResult + callCount); // respond with indexed values - }; + } async.retry(times, fn, function(err, result){ test.equal(callCount, 3, "did not retry the correct number of times"); test.equal(err, error + times, "Incorrect error was returned"); @@ -1511,7 +1511,7 @@ exports['forEachOfLimit synchronous'] = function(test){ exports['forEachOfLimit with array'] = function(test){ var args = []; - var arr = [ "a", "b" ] + var arr = [ "a", "b" ]; async.forEachOfLimit(arr, 1, forEachOfIterator.bind(this, args), function (err) { test.same(args, [ 0, "a", 1, "b" ]); test.done(); @@ -1663,7 +1663,7 @@ exports['reduce'] = function(test){ exports['reduce async with non-reference memo'] = function(test){ async.reduce([1,3,2], 0, function(a, x, callback){ - setTimeout(function(){callback(null, a + x)}, Math.random()*100); + setTimeout(function(){callback(null, a + x);}, Math.random()*100); }, function(err, result){ test.equals(result, 6); test.done(); @@ -1916,7 +1916,7 @@ exports['sortBy inverted'] = function(test){ exports['apply'] = function(test){ test.expect(6); var fn = function(){ - test.same(Array.prototype.slice.call(arguments), [1,2,3,4]) + test.same(Array.prototype.slice.call(arguments), [1,2,3,4]); }; async.apply(fn, 1, 2, 3, 4)(); async.apply(fn, 1, 2, 3)(4); @@ -1924,7 +1924,7 @@ exports['apply'] = function(test){ async.apply(fn, 1)(2, 3, 4); async.apply(fn)(1, 2, 3, 4); test.equals( - async.apply(function(name){return 'hello ' + name}, 'world')(), + async.apply(function(name){return 'hello ' + name;}, 'world')(), 'hello world' ); test.done(); @@ -1994,14 +1994,14 @@ var console_fn_tests = function(name){ exports['times'] = function(test) { - var indices = [] + var indices = []; async.times(5, function(n, next) { - next(null, n) + next(null, n); }, function(err, results) { - test.same(results, [0,1,2,3,4]) - test.done() - }) -} + test.same(results, [0,1,2,3,4]); + test.done(); + }); +}; exports['times'] = function(test){ var args = []; @@ -2089,9 +2089,6 @@ exports['nextTick in the browser'] = function(test){ call_order.push('one'); setTimeout(function(){ - if (typeof process !== 'undefined') { - process.nextTick = _nextTick; - } test.same(call_order, ['one','two']); }, 50); setTimeout(test.done, 100); @@ -2643,12 +2640,12 @@ exports['queue bulk task'] = function (test) { exports['queue idle'] = function(test) { var q = async.queue(function (task, callback) { // Queue is busy when workers are running - test.equal(q.idle(), false) + test.equal(q.idle(), false); callback(); }, 1); // Queue is idle before anything added - test.equal(q.idle(), true) + test.equal(q.idle(), true); q.unshift(4); q.unshift(3); @@ -2656,14 +2653,14 @@ exports['queue idle'] = function(test) { q.unshift(1); // Queue is busy when tasks added - test.equal(q.idle(), false) + test.equal(q.idle(), false); q.drain = function() { // Queue is idle after drain test.equal(q.idle(), true); test.done(); - } -} + }; +}; exports['queue pause'] = function(test) { var call_order = [], @@ -2716,7 +2713,7 @@ exports['queue pause'] = function(test) { ]); test.done(); }, 800); -} +}; exports['queue pause with concurrency'] = function(test) { var call_order = [], @@ -2763,7 +2760,7 @@ exports['queue pause with concurrency'] = function(test) { ]); test.done(); }, 800); -} +}; exports['queue kill'] = function (test) { var q = async.queue(function (task, callback) { @@ -2774,7 +2771,7 @@ exports['queue kill'] = function (test) { }, 1); q.drain = function() { test.ok(false, "Function should never be called"); - } + }; q.push(0); @@ -2783,7 +2780,7 @@ exports['queue kill'] = function (test) { setTimeout(function() { test.equal(q.length(), 0); test.done(); - }, 600) + }, 600); }; exports['priorityQueue'] = function (test) { @@ -3034,7 +3031,7 @@ exports['cargo drain once'] = function (test) { var drainCounter = 0; c.drain = function () { drainCounter++; - } + }; for(var i = 0; i < 10; i++){ c.push(i); @@ -3061,7 +3058,7 @@ exports['cargo drain twice'] = function (test) { var drainCounter = 0; c.drain = function () { drainCounter++; - } + }; loadCargo(); setTimeout(loadCargo, 500); @@ -3160,7 +3157,7 @@ exports['unmemoize'] = function(test) { }); }); }); -} +}; exports['unmemoize a not memoized function'] = function(test) { test.expect(1); @@ -3175,7 +3172,7 @@ exports['unmemoize a not memoized function'] = function(test) { }); test.done(); -} +}; exports['memoize error'] = function (test) { test.expect(1); @@ -3244,22 +3241,22 @@ exports['falsy return values in series'] = function (test) { async.nextTick(function() { callback(null, false); }); - }; + } function taskUndefined(callback) { async.nextTick(function() { callback(null, undefined); }); - }; + } function taskEmpty(callback) { async.nextTick(function() { callback(null); }); - }; + } function taskNull(callback) { async.nextTick(function() { callback(null, null); }); - }; + } async.series( [taskFalse, taskUndefined, taskEmpty, taskNull], function(err, results) { @@ -3279,22 +3276,22 @@ exports['falsy return values in parallel'] = function (test) { async.nextTick(function() { callback(null, false); }); - }; + } function taskUndefined(callback) { async.nextTick(function() { callback(null, undefined); }); - }; + } function taskEmpty(callback) { async.nextTick(function() { callback(null); }); - }; + } function taskNull(callback) { async.nextTick(function() { callback(null, null); }); - }; + } async.parallel( [taskFalse, taskUndefined, taskEmpty, taskNull], function(err, results) { @@ -3322,12 +3319,12 @@ exports['queue events'] = function(test) { calls.push('saturated'); }; q.empty = function() { - test.ok(q.length() == 0, 'queue should be empty now'); + test.ok(q.length() === 0, 'queue should be empty now'); calls.push('empty'); }; q.drain = function() { test.ok( - q.length() == 0 && q.running() == 0, + q.length() === 0 && q.running() === 0, 'queue should be empty now and no more workers should be running' ); calls.push('drain'); @@ -3365,7 +3362,7 @@ exports['queue empty'] = function(test) { q.drain = function() { test.ok( - q.length() == 0 && q.running() == 0, + q.length() === 0 && q.running() === 0, 'queue should be empty now and no more workers should be running' ); calls.push('drain'); -- cgit v1.2.1 From f49a63ffe8e0cbc9ba8b3da5f78d626a2188e66a Mon Sep 17 00:00:00 2001 From: Alexander Early Date: Tue, 19 May 2015 19:06:09 -0700 Subject: added changelog --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..c712934 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,8 @@ +# v0.10 + +- Started using a changelog! +- Added `forEachOf` for iterating over Objects (or to iterate Arrays with indexes) (#168 #704 #321) +- Detect deadlocks in `auto` (#663) +- Better support for require.js (#527) +- Doc fixes (#542 #596 #615 #628 #631 #690) +- Use single noop function internally (#546) -- cgit v1.2.1 From 8837212d58c354a7477e05d9450fd826c0c11fe0 Mon Sep 17 00:00:00 2001 From: Suguru Motegi Date: Wed, 20 May 2015 11:28:13 +0900 Subject: perf(slice): fix not to use `Array#slice` --- lib/async.js | 59 ++++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 38 insertions(+), 21 deletions(-) diff --git a/lib/async.js b/lib/async.js index d6cd961..47ab5a7 100644 --- a/lib/async.js +++ b/lib/async.js @@ -97,6 +97,23 @@ return keys; }; + var _baseSlice = function (arr, start) { + start = start || 0; + var index = -1; + var length = arr.length; + + if (start) { + length -= start; + length = length < 0 ? 0 : length; + } + var result = Array(length); + + while (++index < length) { + result[index] = arr[index + start]; + } + return result; + }; + //// exported async module functions //// //// nextTick implementation with browser-compatible fallback //// @@ -338,19 +355,19 @@ var doParallel = function (fn) { return function () { - var args = Array.prototype.slice.call(arguments); + var args = _baseSlice(arguments); return fn.apply(null, [async.each].concat(args)); }; }; var doParallelLimit = function(limit, fn) { return function () { - var args = Array.prototype.slice.call(arguments); + var args = _baseSlice(arguments); return fn.apply(null, [_eachLimit(limit)].concat(args)); }; }; var doSeries = function (fn) { return function () { - var args = Array.prototype.slice.call(arguments); + var args = _baseSlice(arguments); return fn.apply(null, [async.eachSeries].concat(args)); }; }; @@ -581,7 +598,7 @@ _each(keys, function (k) { var task = _isArray(tasks[k]) ? tasks[k]: [tasks[k]]; var taskCallback = function (err) { - var args = Array.prototype.slice.call(arguments, 1); + var args = _baseSlice(arguments, 1); if (args.length <= 1) { args = args[0]; } @@ -679,7 +696,7 @@ callback = noop; } else { - var args = Array.prototype.slice.call(arguments, 1); + var args = _baseSlice(arguments, 1); var next = iterator.next(); if (next) { args.push(wrapIterator(next)); @@ -702,7 +719,7 @@ eachfn.map(tasks, function (fn, callback) { if (fn) { fn(function (err) { - var args = Array.prototype.slice.call(arguments, 1); + var args = _baseSlice(arguments, 1); if (args.length <= 1) { args = args[0]; } @@ -715,7 +732,7 @@ var results = {}; eachfn.each(_keys(tasks), function (k, callback) { tasks[k](function (err) { - var args = Array.prototype.slice.call(arguments, 1); + var args = _baseSlice(arguments, 1); if (args.length <= 1) { args = args[0]; } @@ -742,7 +759,7 @@ async.mapSeries(tasks, function (fn, callback) { if (fn) { fn(function (err) { - var args = Array.prototype.slice.call(arguments, 1); + var args = _baseSlice(arguments, 1); if (args.length <= 1) { args = args[0]; } @@ -755,7 +772,7 @@ var results = {}; async.eachSeries(_keys(tasks), function (k, callback) { tasks[k](function (err) { - var args = Array.prototype.slice.call(arguments, 1); + var args = _baseSlice(arguments, 1); if (args.length <= 1) { args = args[0]; } @@ -785,10 +802,10 @@ }; async.apply = function (fn) { - var args = Array.prototype.slice.call(arguments, 1); + var args = _baseSlice(arguments, 1); return function () { return fn.apply( - null, args.concat(Array.prototype.slice.call(arguments)) + null, args.concat(_baseSlice(arguments)) ); }; }; @@ -826,7 +843,7 @@ if (err) { return callback(err); } - var args = Array.prototype.slice.call(arguments, 1); + var args = _baseSlice(arguments, 1); if (test.apply(null, args)) { async.doWhilst(iterator, test, callback); } @@ -855,7 +872,7 @@ if (err) { return callback(err); } - var args = Array.prototype.slice.call(arguments, 1); + var args = _baseSlice(arguments, 1); if (!test.apply(null, args)) { async.doUntil(iterator, test, callback); } @@ -1104,9 +1121,9 @@ var _console_fn = function (name) { return function (fn) { - var args = Array.prototype.slice.call(arguments, 1); + var args = _baseSlice(arguments, 1); fn.apply(null, args.concat([function (err) { - var args = Array.prototype.slice.call(arguments, 1); + var args = _baseSlice(arguments, 1); if (typeof console !== 'undefined') { if (err) { if (console.error) { @@ -1135,7 +1152,7 @@ return x; }; var memoized = function () { - var args = Array.prototype.slice.call(arguments); + var args = _baseSlice(arguments); var callback = args.pop(); var key = hasher.apply(null, args); if (key in memo) { @@ -1149,7 +1166,7 @@ else { queues[key] = [callback]; fn.apply(null, args.concat([function () { - memo[key] = arguments; + memo[key] = _baseSlice(arguments); var q = queues[key]; delete queues[key]; for (var i = 0, l = q.length; i < l; i++) { @@ -1189,12 +1206,12 @@ var fns = arguments; return function () { var that = this; - var args = Array.prototype.slice.call(arguments); + var args = _baseSlice(arguments); var callback = args.pop(); async.reduce(fns, args, function (newargs, fn, cb) { fn.apply(that, newargs.concat([function () { var err = arguments[0]; - var nextargs = Array.prototype.slice.call(arguments, 1); + var nextargs = _baseSlice(arguments, 1); cb(err, nextargs); }])); }, @@ -1211,7 +1228,7 @@ var _applyEach = function (eachfn, fns /*args...*/) { var go = function () { var that = this; - var args = Array.prototype.slice.call(arguments); + var args = _baseSlice(arguments); var callback = args.pop(); return eachfn(fns, function (fn, cb) { fn.apply(that, args.concat([cb])); @@ -1219,7 +1236,7 @@ callback); }; if (arguments.length > 2) { - var args = Array.prototype.slice.call(arguments, 2); + var args = _baseSlice(arguments, 2); return go.apply(this, args); } else { -- cgit v1.2.1 From a73eef321256b36f529dce57a1e6ad7efd34851a Mon Sep 17 00:00:00 2001 From: Alexander Early Date: Tue, 19 May 2015 19:45:39 -0700 Subject: cleaning up build files --- .gitignore | 4 ++-- Makefile | 13 ++++++------- package.json | 1 - 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/.gitignore b/.gitignore index 8225baa..f06235c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ -/node_modules -/dist +node_modules +dist diff --git a/Makefile b/Makefile index 4b0f23c..ac16a07 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,8 @@ PACKAGE = asyncjs -NODEJS = $(if $(shell test -f /usr/bin/nodejs && echo "true"),nodejs,node) -CWD := "$(shell pwd)" -NODEUNIT = $(CWD)/node_modules/nodeunit/bin/nodeunit -UGLIFY = $(CWD)/node_modules/uglify-js/bin/uglifyjs -NODELINT = $(CWD)/node_modules/nodelint/nodelint +CWD := $(shell pwd) +NODEUNIT = "$(CWD)/node_modules/.bin/nodeunit" +UGLIFY = "$(CWD)/node_modules/.bin/uglifyjs" +JSHINT = "$(CWD)/node_modules/.bin/jshint" BUILDDIR = dist @@ -20,6 +19,6 @@ clean: rm -rf $(BUILDDIR) lint: - $(NODELINT) --config nodelint.cfg lib/async.js + $(JSHINT) lib/*.js test/*.js -.PHONY: test build all +.PHONY: test lint build all clean diff --git a/package.json b/package.json index ffb6491..7385df8 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,6 @@ "devDependencies": { "jshint": "~2.7.0", "lodash": ">=2.4.1", - "nodelint": ">0.0.0", "nodeunit": ">0.0.0", "uglify-js": "1.2.x" }, -- cgit v1.2.1 From 1f98d6a54dc8f74c582fa28f23893d2292cd7f81 Mon Sep 17 00:00:00 2001 From: Alexander Early Date: Tue, 19 May 2015 22:13:00 -0700 Subject: adding benchmark script --- .gitignore | 1 + nodelint.cfg | 4 -- package.json | 2 + perf/benchmark.js | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ perf/suites.js | 40 ++++++++++++++++++++ 5 files changed, 151 insertions(+), 4 deletions(-) delete mode 100644 nodelint.cfg create mode 100755 perf/benchmark.js create mode 100644 perf/suites.js diff --git a/.gitignore b/.gitignore index f06235c..291d97e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules dist +perf/versions diff --git a/nodelint.cfg b/nodelint.cfg deleted file mode 100644 index 457a967..0000000 --- a/nodelint.cfg +++ /dev/null @@ -1,4 +0,0 @@ -var options = { - indent: 4, - onevar: false -}; diff --git a/package.json b/package.json index 7385df8..b797250 100644 --- a/package.json +++ b/package.json @@ -19,8 +19,10 @@ }, "license": "MIT", "devDependencies": { + "benchmark": "~1.0.0", "jshint": "~2.7.0", "lodash": ">=2.4.1", + "mkdirp": "~0.5.1", "nodeunit": ">0.0.0", "uglify-js": "1.2.x" }, diff --git a/perf/benchmark.js b/perf/benchmark.js new file mode 100755 index 0000000..44dbc61 --- /dev/null +++ b/perf/benchmark.js @@ -0,0 +1,108 @@ +#!/usr/bin/env node + +var Benchmark = require("benchmark"); +var benchOptions = {defer: true, minSamples: 7}; +var exec = require("child_process").exec; +var fs = require("fs"); +var mkdirp = require("mkdirp"); +var async = require("../"); +var suiteConfigs = require("./suites"); + +var version1 = process.argv[2] || require("../package.json").version; +var version2 = process.argv[3] || "current"; +var versionNames = [version1, version2]; +var versions; +var wins = [0, 0]; + +console.log("Comparing " + version1 + " with " + version2); +console.log("--------------------------------------"); + + +async.eachSeries(versionNames, cloneVersion, function (err) { + versions = versionNames.map(requireVersion); + + var suites = suiteConfigs.map(createSuite); + + async.eachSeries(suites, runSuite, function () { + var wins0 = wins[0].length; + var wins1 = wins[1].length; + + if (wins0 > wins1) { + console.log(versionNames[0] + " faster overall " + + "(" + wins0 + " wins vs. " + wins1 +" wins)"); + } else if (wins1 > wins0) { + console.log(versionNames[1] + " faster overall " + + "(" + wins1 + " wins vs. " + wins0 +" wins)"); + } else { + console.log("Both versions are equal"); + } + }); +}); + +function runSuite(suite, callback) { + suite.on("complete", function () { + callback(); + }).run({async: true}); +} + +function createSuite(suiteConfig) { + var suite = new Benchmark.Suite(); + + function addBench(version, versionName) { + var title = suiteConfig.name + " " + versionName; + suite.add(title, function (deferred) { + suiteConfig.fn(versions[0], deferred); + }, benchOptions); + } + + addBench(versions[0], versionNames[0]); + addBench(versions[1], versionNames[1]); + + return suite.on('cycle', function(event) { + console.log(event.target + ""); + }) + .on('complete', function() { + var fastest = this.filter('fastest'); + if (fastest.length === 2) { + console.log("Tie"); + } else { + console.log(fastest[0].name + ' is faster'); + var index = this.indexOf("fastest"); + wins[index]++; + } + console.log("--------------------------------------"); + }); + +} + +function requireVersion(tag) { + if (tag === "current") { + return async; + } + + return require("./versions/" + tag + "/"); +} + +function cloneVersion(tag, callback) { + if (tag === "current") return callback(); + + var versionDir = __dirname + "/versions/" + tag; + mkdirp.sync(versionDir); + fs.open(versionDir + "/package.json", "r", function (err, handle) { + if (!err) { + // version has already been cloned + fs.close(handle); + return callback(); + } + + var cmd = "git clone --branch " + tag + " ../ " + versionDir; + + exec(cmd, function (err, stdout, stderr) { + if (err) { + throw err; + } + callback(); + }); + + }); +} diff --git a/perf/suites.js b/perf/suites.js new file mode 100644 index 0000000..d9a7b19 --- /dev/null +++ b/perf/suites.js @@ -0,0 +1,40 @@ +module.exports = [ + { + name: "each(10)", + fn: function (async, deferred) { + async.each(Array(10), function (num, cb) { + async.setImmediate(cb); + }, done(deferred)); + } + }, + { + name: "each(10000)", + fn: function (async, deferred) { + async.each(Array(10000), function (num, cb) { + async.setImmediate(cb); + }, done(deferred)); + } + }, + { + name: "eachSeries(10)", + fn: function (async, deferred) { + async.eachSeries(Array(10), function (num, cb) { + async.setImmediate(cb); + }, done(deferred)); + } + }, + { + name: "eachSeries(10000)", + fn: function (async, deferred) { + async.eachSeries(Array(10000), function (num, cb) { + async.setImmediate(cb); + }, done(deferred)); + } + } +]; + +function done(deferred) { + return function () { + deferred.resolve(); + }; +} -- cgit v1.2.1 From 1b9200b16db246f660ee287b619e8edba3a19c0a Mon Sep 17 00:00:00 2001 From: Alexander Early Date: Tue, 19 May 2015 23:30:29 -0700 Subject: fixed path in benchmark script --- perf/benchmark.js | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/perf/benchmark.js b/perf/benchmark.js index 44dbc61..f31037b 100755 --- a/perf/benchmark.js +++ b/perf/benchmark.js @@ -1,9 +1,29 @@ #!/usr/bin/env node +/** + * Compare the performance of any two tagged versions of async. Can also + * compare any tag with what is in the current working directory. + * + * Usage: + * + * perf/benchmark.js 0.9.2 0.9.0 + * + * Compare version 0.9.0 with version 0.9.2 + * + * perf/benchmark.js 0.6.0 + * + * Compare version 0.6.0 with the current working version + * + * perf/benchmark.js + * + * Compare the current working version with the latest tagged version. + */ + var Benchmark = require("benchmark"); var benchOptions = {defer: true, minSamples: 7}; var exec = require("child_process").exec; var fs = require("fs"); +var path = require("path"); var mkdirp = require("mkdirp"); var async = require("../"); var suiteConfigs = require("./suites"); @@ -95,7 +115,9 @@ function cloneVersion(tag, callback) { return callback(); } - var cmd = "git clone --branch " + tag + " ../ " + versionDir; + var repoPath = path.join(__dirname, ".."); + + var cmd = "git clone --branch " + tag + " " + repoPath + " " + versionDir; exec(cmd, function (err, stdout, stderr) { if (err) { -- cgit v1.2.1 From 62971059e005198635b30b2b01920f054b5d6eaf Mon Sep 17 00:00:00 2001 From: Alexander Early Date: Wed, 20 May 2015 00:31:56 -0700 Subject: added more benchmarks, improved benchmark reporting --- perf/benchmark.js | 41 ++++++++++++--------- perf/suites.js | 105 +++++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 117 insertions(+), 29 deletions(-) diff --git a/perf/benchmark.js b/perf/benchmark.js index f31037b..8900062 100755 --- a/perf/benchmark.js +++ b/perf/benchmark.js @@ -19,8 +19,9 @@ * Compare the current working version with the latest tagged version. */ +var _ = require("lodash"); var Benchmark = require("benchmark"); -var benchOptions = {defer: true, minSamples: 7}; +var benchOptions = {defer: true, minSamples: 1, maxTime: 1}; var exec = require("child_process").exec; var fs = require("fs"); var path = require("path"); @@ -28,13 +29,15 @@ var mkdirp = require("mkdirp"); var async = require("../"); var suiteConfigs = require("./suites"); -var version1 = process.argv[2] || require("../package.json").version; -var version2 = process.argv[3] || "current"; -var versionNames = [version1, version2]; +var version0 = process.argv[2] || require("../package.json").version; +var version1 = process.argv[3] || "current"; +var versionNames = [version0, version1]; var versions; -var wins = [0, 0]; +var wins = {}; +wins[version0] = 0; +wins[version1] = 0; -console.log("Comparing " + version1 + " with " + version2); +console.log("Comparing " + version0 + " with " + version1); console.log("--------------------------------------"); @@ -44,14 +47,14 @@ async.eachSeries(versionNames, cloneVersion, function (err) { var suites = suiteConfigs.map(createSuite); async.eachSeries(suites, runSuite, function () { - var wins0 = wins[0].length; - var wins1 = wins[1].length; + var wins0 = wins[version0]; + var wins1 = wins[version1]; if (wins0 > wins1) { - console.log(versionNames[0] + " faster overall " + + console.log(version0 + " faster overall " + "(" + wins0 + " wins vs. " + wins1 +" wins)"); } else if (wins1 > wins0) { - console.log(versionNames[1] + " faster overall " + + console.log(version1 + " faster overall " + "(" + wins1 + " wins vs. " + wins0 +" wins)"); } else { console.log("Both versions are equal"); @@ -71,24 +74,30 @@ function createSuite(suiteConfig) { function addBench(version, versionName) { var title = suiteConfig.name + " " + versionName; suite.add(title, function (deferred) { - suiteConfig.fn(versions[0], deferred); - }, benchOptions); + suiteConfig.fn(versions[0], function () { + deferred.resolve(); + }); + }, _.extend({ + versionName: versionName, + setup: suiteConfig.setup + }, benchOptions)); } addBench(versions[0], versionNames[0]); addBench(versions[1], versionNames[1]); return suite.on('cycle', function(event) { - console.log(event.target + ""); + var mean = event.target.stats.mean * 1000; + console.log(event.target + ", " + mean.toFixed(1) + "ms per sample"); }) .on('complete', function() { var fastest = this.filter('fastest'); if (fastest.length === 2) { console.log("Tie"); } else { - console.log(fastest[0].name + ' is faster'); - var index = this.indexOf("fastest"); - wins[index]++; + var winner = fastest[0].options.versionName; + console.log(winner + ' is faster'); + wins[winner]++; } console.log("--------------------------------------"); }); diff --git a/perf/suites.js b/perf/suites.js index d9a7b19..884b65e 100644 --- a/perf/suites.js +++ b/perf/suites.js @@ -1,40 +1,119 @@ +var _ = require("lodash"); +var parallel1000Funcs = _.range(1000).map(function () { + return function (cb) { cb(); }; +}); +var parallel10Funcs = _.range(10).map(function () { + return function (cb) { cb(); }; +}); + module.exports = [ { name: "each(10)", - fn: function (async, deferred) { + fn: function (async, done) { async.each(Array(10), function (num, cb) { async.setImmediate(cb); - }, done(deferred)); + }, done); } }, { name: "each(10000)", - fn: function (async, deferred) { + fn: function (async, done) { async.each(Array(10000), function (num, cb) { async.setImmediate(cb); - }, done(deferred)); + }, done); } }, { name: "eachSeries(10)", - fn: function (async, deferred) { + fn: function (async, done) { async.eachSeries(Array(10), function (num, cb) { async.setImmediate(cb); - }, done(deferred)); + }, done); } }, { name: "eachSeries(10000)", - fn: function (async, deferred) { + fn: function (async, done) { async.eachSeries(Array(10000), function (num, cb) { async.setImmediate(cb); - }, done(deferred)); + }, done); + } + }, + { + name: "parallel(10)", + fn: function (async, done) { + async.parallel(parallel10Funcs, done); + } + }, + { + name: "parallel(1000)", + fn: function (async, done) { + async.parallel(parallel1000Funcs, done); + } + }, + { + name: "queue(1000)", + fn: function (async, done) { + var numEntries = 1000; + var q = async.queue(worker, 1); + for (var i = 1; i <= numEntries; i++) { + q.push({num: i}); + } + function worker(task, callback) { + if (task.num === numEntries) { + return done(); + } + setImmediate(callback); + } + } + }, + { + name: "queue(30000)", + fn: function (async, done) { + var numEntries = 30000; + var q = async.queue(worker, 1); + for (var i = 1; i <= numEntries; i++) { + q.push({num: i}); + } + function worker(task, callback) { + if (task.num === numEntries) { + return done(); + } + setImmediate(callback); + } + } + }, + { + name: "queue(100000)", + fn: function (async, done) { + var numEntries = 100000; + var q = async.queue(worker, 1); + for (var i = 1; i <= numEntries; i++) { + q.push({num: i}); + } + function worker(task, callback) { + if (task.num === numEntries) { + return done(); + } + setImmediate(callback); + } + } + }, + { + name: "queue(200000)", + fn: function (async, done) { + var numEntries = 200000; + var q = async.queue(worker, 1); + for (var i = 1; i <= numEntries; i++) { + q.push({num: i}); + } + function worker(task, callback) { + if (task.num === numEntries) { + return done(); + } + setImmediate(callback); + } } } ]; -function done(deferred) { - return function () { - deferred.resolve(); - }; -} -- cgit v1.2.1 From 562f879df5f1fd2aa6d2b411e67357b78a9bc249 Mon Sep 17 00:00:00 2001 From: Vito Alexander Nordloh Date: Wed, 20 May 2015 23:22:52 +0200 Subject: intercept queue concurrency of 0 --- lib/async.js | 3 +++ test/test-async.js | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/lib/async.js b/lib/async.js index 47ab5a7..81916fd 100644 --- a/lib/async.js +++ b/lib/async.js @@ -886,6 +886,9 @@ if (concurrency === undefined) { concurrency = 1; } + else if(concurrency === 0) { + throw new Error('Concurrency must not be zero'); + } function _insert(q, data, pos, callback) { if (!q.started){ q.started = true; diff --git a/test/test-async.js b/test/test-async.js index 6f6a741..64c33f9 100755 --- a/test/test-async.js +++ b/test/test-async.js @@ -2453,6 +2453,13 @@ exports['queue default concurrency'] = function (test) { }; }; +exports['queue zero concurrency'] = function(test){ + test.throws(function () { + async.queue(function (task, callback) {}, 0); + }); + test.done(); +}; + exports['queue error propagation'] = function(test){ var results = []; -- cgit v1.2.1 From f5c6331d3e0c8af238a143b16cf7271902acf797 Mon Sep 17 00:00:00 2001 From: Alexander Early Date: Wed, 20 May 2015 15:31:25 -0700 Subject: improved benchmark code, added more benchmarks --- perf/benchmark.js | 60 ++++++++++++++++++++------- perf/suites.js | 122 ++++++++++++++++++++++++------------------------------ 2 files changed, 97 insertions(+), 85 deletions(-) diff --git a/perf/benchmark.js b/perf/benchmark.js index 8900062..903d04a 100755 --- a/perf/benchmark.js +++ b/perf/benchmark.js @@ -21,7 +21,7 @@ var _ = require("lodash"); var Benchmark = require("benchmark"); -var benchOptions = {defer: true, minSamples: 1, maxTime: 1}; +var benchOptions = {defer: true, minSamples: 1, maxTime: 2}; var exec = require("child_process").exec; var fs = require("fs"); var path = require("path"); @@ -34,8 +34,9 @@ var version1 = process.argv[3] || "current"; var versionNames = [version0, version1]; var versions; var wins = {}; -wins[version0] = 0; -wins[version1] = 0; +var totalTime = {}; +totalTime[version0] = wins[version0] = 0; +totalTime[version1] = wins[version1] = 0; console.log("Comparing " + version0 + " with " + version1); console.log("--------------------------------------"); @@ -44,20 +45,26 @@ console.log("--------------------------------------"); async.eachSeries(versionNames, cloneVersion, function (err) { versions = versionNames.map(requireVersion); - var suites = suiteConfigs.map(createSuite); + var suites = suiteConfigs + .map(setDefaultOptions) + .reduce(handleMultipleArgs, []) + .map(setName) + .map(createSuite); async.eachSeries(suites, runSuite, function () { - var wins0 = wins[version0]; - var wins1 = wins[version1]; - - if (wins0 > wins1) { + var totalTime0 = Math.round(totalTime[version0]); + var totalTime1 = Math.round(totalTime[version1]); + + if ( Math.abs((totalTime0 / totalTime1) - 1) < 0.01) { + // if < 1% difference, we're likely within the margins of error + console.log("Both versions are about equal " + + "(" + totalTime0 + "ms total vs. " + totalTime1 + "ms total)"); + } else if (totalTime0 < totalTime1) { console.log(version0 + " faster overall " + - "(" + wins0 + " wins vs. " + wins1 +" wins)"); - } else if (wins1 > wins0) { + "(" + totalTime0 + "ms total vs. " + totalTime1 + "ms total)"); + } else if (totalTime1 < totalTime0) { console.log(version1 + " faster overall " + - "(" + wins1 + " wins vs. " + wins0 +" wins)"); - } else { - console.log("Both versions are equal"); + "(" + totalTime1 + "ms total vs. " + totalTime0 + "ms total)"); } }); }); @@ -68,27 +75,48 @@ function runSuite(suite, callback) { }).run({async: true}); } +function setDefaultOptions(suiteConfig) { + suiteConfig.args = suiteConfig.args || [[]]; + suiteConfig.setup = suiteConfig.setup || function () {}; + return suiteConfig; +} + +function handleMultipleArgs(list, suiteConfig) { + return list.concat(suiteConfig.args.map(function (args) { + return _.defaults({args: args}, suiteConfig); + })); +} + +function setName(suiteConfig) { + suiteConfig.name = suiteConfig.name + "(" + suiteConfig.args.join(",") + ")"; + return suiteConfig; +} + function createSuite(suiteConfig) { var suite = new Benchmark.Suite(); + var args = suiteConfig.args; function addBench(version, versionName) { - var title = suiteConfig.name + " " + versionName; - suite.add(title, function (deferred) { + var name = suiteConfig.name + " " + versionName; + suite.add(name, function (deferred) { suiteConfig.fn(versions[0], function () { deferred.resolve(); }); }, _.extend({ versionName: versionName, - setup: suiteConfig.setup + setup: _.partial.apply(null, [suiteConfig.setup].concat(args)) }, benchOptions)); } addBench(versions[0], versionNames[0]); addBench(versions[1], versionNames[1]); + return suite.on('cycle', function(event) { var mean = event.target.stats.mean * 1000; console.log(event.target + ", " + mean.toFixed(1) + "ms per sample"); + var version = event.target.options.versionName; + totalTime[version] += mean; }) .on('complete', function() { var fastest = this.filter('fastest'); diff --git a/perf/suites.js b/perf/suites.js index 884b65e..2e1beaa 100644 --- a/perf/suites.js +++ b/perf/suites.js @@ -1,14 +1,14 @@ var _ = require("lodash"); -var parallel1000Funcs = _.range(1000).map(function () { - return function (cb) { cb(); }; -}); -var parallel10Funcs = _.range(10).map(function () { - return function (cb) { cb(); }; -}); +var tasks; module.exports = [ { - name: "each(10)", + name: "each", + // args lists are passed to the setup function + args: [[10], [300], [10000]], + setup: function(count) { + tasks = Array(count); + }, fn: function (async, done) { async.each(Array(10), function (num, cb) { async.setImmediate(cb); @@ -16,93 +16,77 @@ module.exports = [ } }, { - name: "each(10000)", + name: "eachSeries", + args: [[10], [300], [10000]], + setup: function(count) { + tasks = Array(count); + }, fn: function (async, done) { - async.each(Array(10000), function (num, cb) { + async.eachSeries(tasks, function (num, cb) { async.setImmediate(cb); }, done); } }, { - name: "eachSeries(10)", + name: "eachLimit", + args: [[10], [300], [10000]], + setup: function(count) { + tasks = Array(count); + }, fn: function (async, done) { - async.eachSeries(Array(10), function (num, cb) { + async.eachLimit(tasks, 4, function (num, cb) { async.setImmediate(cb); }, done); } }, { - name: "eachSeries(10000)", + name: "parallel", + args: [[10], [100], [1000]], + setup: function (count) { + tasks = _.range(count).map(function () { + return function (cb) { cb(); }; + }); + }, fn: function (async, done) { - async.eachSeries(Array(10000), function (num, cb) { - async.setImmediate(cb); - }, done); - } - }, - { - name: "parallel(10)", - fn: function (async, done) { - async.parallel(parallel10Funcs, done); - } - }, - { - name: "parallel(1000)", - fn: function (async, done) { - async.parallel(parallel1000Funcs, done); - } - }, - { - name: "queue(1000)", - fn: function (async, done) { - var numEntries = 1000; - var q = async.queue(worker, 1); - for (var i = 1; i <= numEntries; i++) { - q.push({num: i}); - } - function worker(task, callback) { - if (task.num === numEntries) { - return done(); - } - setImmediate(callback); - } + async.parallel(tasks, done); } }, { - name: "queue(30000)", + name: "series", + args: [[10], [100], [1000]], + setup: function (count) { + tasks = _.range(count).map(function () { + return function (cb) { cb(); }; + }); + }, fn: function (async, done) { - var numEntries = 30000; - var q = async.queue(worker, 1); - for (var i = 1; i <= numEntries; i++) { - q.push({num: i}); - } - function worker(task, callback) { - if (task.num === numEntries) { - return done(); - } - setImmediate(callback); - } + async.series(tasks, done); } }, { - name: "queue(100000)", + name: "waterfall", + args: [[10], [100], [1000]], + setup: function (count) { + tasks = [ + function (cb) { + return cb(null, 1); + } + ].concat(_.range(count).map(function (i) { + return function (arg, cb) { cb(null, i); }; + })); + }, fn: function (async, done) { - var numEntries = 100000; - var q = async.queue(worker, 1); - for (var i = 1; i <= numEntries; i++) { - q.push({num: i}); - } - function worker(task, callback) { - if (task.num === numEntries) { - return done(); - } - setImmediate(callback); - } + async.waterfall(tasks, done); } }, { - name: "queue(200000)", + name: "queue", + args: [[1000], [30000], [100000], [200000]], + setup: function (count) { + tasks = count; + }, fn: function (async, done) { - var numEntries = 200000; + var numEntries = tasks; var q = async.queue(worker, 1); for (var i = 1; i <= numEntries; i++) { q.push({num: i}); -- cgit v1.2.1 From 3ffbf2b2fddf68d824c98903c60a9d35b34570e6 Mon Sep 17 00:00:00 2001 From: Alexander Early Date: Wed, 20 May 2015 15:42:15 -0700 Subject: optmize internal _each, _map, _keys, _forEachOf functions --- lib/async.js | 41 ++++++++++++++++++----------------------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/lib/async.js b/lib/async.js index 81916fd..a1650a7 100644 --- a/lib/async.js +++ b/lib/async.js @@ -50,26 +50,26 @@ }; var _each = function (arr, iterator) { - for (var i = 0; i < arr.length; i += 1) { - iterator(arr[i], i, arr); - } + var index = -1, + length = arr.length; + + while (++index < length) { + iterator(arr[index], index, arr); + } }; var _map = function (arr, iterator) { - if (arr.map) { - return arr.map(iterator); - } - var results = []; - _each(arr, function (x, i, a) { - results.push(iterator(x, i, a)); - }); - return results; + var index = -1, + length = arr.length, + result = Array(length); + + while (++index < length) { + result[index] = iterator(arr[index], index, arr); + } + return result; }; var _reduce = function (arr, iterator, memo) { - if (arr.reduce) { - return arr.reduce(iterator, memo); - } _each(arr, function (x, i, a) { memo = iterator(memo, x, i, a); }); @@ -77,17 +77,12 @@ }; var _forEachOf = function (object, iterator) { - for (var key in object) { - if (object.hasOwnProperty(key)) { - iterator(object[key], key); - } - } + _each(_keys(object), function (key) { + iterator(object[key], key); + }); }; - var _keys = function (obj) { - if (Object.keys) { - return Object.keys(obj); - } + var _keys = Object.keys || function (obj) { var keys = []; for (var k in obj) { if (obj.hasOwnProperty(k)) { -- cgit v1.2.1 From 39715f48f23dc36cb0e8ebe44dde16ca07a04b06 Mon Sep 17 00:00:00 2001 From: Alexander Early Date: Wed, 20 May 2015 15:46:46 -0700 Subject: update ignore files, update changelog --- .npmignore | 2 +- CHANGELOG.md | 12 +++++++++--- Makefile | 2 +- package.json | 5 +++-- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/.npmignore b/.npmignore index 392d66e..5fc10cf 100644 --- a/.npmignore +++ b/.npmignore @@ -1,7 +1,7 @@ deps dist test -nodelint.cfg +perf .npmignore .gitmodules Makefile diff --git a/CHANGELOG.md b/CHANGELOG.md index c712934..d3ce9d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,14 @@ -# v0.10 +# v1.0.0 -- Started using a changelog! -- Added `forEachOf` for iterating over Objects (or to iterate Arrays with indexes) (#168 #704 #321) +No known breaking changes, we are simply complying with semver from here on out. + +Changes: + +- Start using a changelog! +- Add `forEachOf` for iterating over Objects (or to iterate Arrays with indexes available) (#168 #704 #321) - Detect deadlocks in `auto` (#663) - Better support for require.js (#527) +- Throw if queue created with concurrency `0` (#714) - Doc fixes (#542 #596 #615 #628 #631 #690) - Use single noop function internally (#546) +- Optimize internal `_each`, `_map` and `_keys` functions. diff --git a/Makefile b/Makefile index ac16a07..4356239 100644 --- a/Makefile +++ b/Makefile @@ -19,6 +19,6 @@ clean: rm -rf $(BUILDDIR) lint: - $(JSHINT) lib/*.js test/*.js + $(JSHINT) lib/*.js test/*.js perf/*.js .PHONY: test lint build all clean diff --git a/package.json b/package.json index b797250..ec89faf 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ }, "scripts": { "test": "npm run-script lint && nodeunit test/test-async.js", - "lint": "jshint lib/async.js test/test-async.js" + "lint": "jshint lib/*.js test/*.js perf/*.js" }, "spm": { "main": "lib/async.js" @@ -51,7 +51,8 @@ "node_modules", "bower_components", "test", - "tests" + "tests", + "perf" ] } } -- cgit v1.2.1 From 91f6fb305f860086fbb388c7ae7665eea39fde74 Mon Sep 17 00:00:00 2001 From: Alexander Early Date: Wed, 20 May 2015 16:20:44 -0700 Subject: fix unneeded iteration in queue.resume. Fixes #758 --- lib/async.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/async.js b/lib/async.js index a1650a7..bbb3495 100644 --- a/lib/async.js +++ b/lib/async.js @@ -974,9 +974,10 @@ resume: function () { if (q.paused === false) { return; } q.paused = false; + var resumeCount = Math.min(q.concurrency, q.tasks.length); // Need to call q.process once per concurrent // worker to preserve full concurrency after pause - for (var w = 1; w <= q.concurrency; w++) { + for (var w = 1; w <= resumeCount; w++) { async.setImmediate(q.process); } } -- cgit v1.2.1 From 29e7141e85c3d4029a5ce09402ff369d4419bbbf Mon Sep 17 00:00:00 2001 From: Alexander Early Date: Wed, 20 May 2015 16:22:35 -0700 Subject: change pronoun. Fixes #729 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cc55da2..7e5e15f 100644 --- a/README.md +++ b/README.md @@ -358,7 +358,7 @@ Like [`forEachOf`](#forEachOf), except the number of `iterator`s running at a gi Produces a new array of values by mapping each value in `arr` through the `iterator` function. The `iterator` is called with an item from `arr` and a callback for when it has finished processing. Each of these callback takes 2 arguments: -an `error`, and the transformed item from `arr`. If `iterator` passes an error to his +an `error`, and the transformed item from `arr`. If `iterator` passes an error to its callback, the main `callback` (for the `map` function) is immediately called with the error. Note, that since this function applies the `iterator` to each item in parallel, -- cgit v1.2.1 From e417af6c1e20374bb04468fe5a789fef701d0bec Mon Sep 17 00:00:00 2001 From: Alexander Early Date: Wed, 20 May 2015 16:33:05 -0700 Subject: guard against setImmediate mocking. Fixes #609 #611 --- lib/async.js | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/async.js b/lib/async.js index bbb3495..1e76ee8 100644 --- a/lib/async.js +++ b/lib/async.js @@ -112,11 +112,18 @@ //// exported async module functions //// //// nextTick implementation with browser-compatible fallback //// + + // capture the global reference to guard against fakeTimer mocks + var _setImmediate; + if (typeof setImmediate === 'function') { + _setImmediate = setImmediate; + } + if (typeof process === 'undefined' || !(process.nextTick)) { - if (typeof setImmediate === 'function') { + if (_setImmediate) { async.nextTick = function (fn) { // not a direct alias for IE10 compatibility - setImmediate(fn); + _setImmediate(fn); }; async.setImmediate = async.nextTick; } @@ -129,10 +136,10 @@ } else { async.nextTick = process.nextTick; - if (typeof setImmediate !== 'undefined') { + if (_setImmediate) { async.setImmediate = function (fn) { // not a direct alias for IE10 compatibility - setImmediate(fn); + _setImmediate(fn); }; } else { -- cgit v1.2.1 From bb33062759891a2c68df44687eb0a7dc20a31810 Mon Sep 17 00:00:00 2001 From: Alexander Early Date: Wed, 20 May 2015 16:39:09 -0700 Subject: update changelog for 1.0.0 --- CHANGELOG.md | 4 +++- support/sync-package-managers.js | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d3ce9d0..7d39c37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ Changes: - Detect deadlocks in `auto` (#663) - Better support for require.js (#527) - Throw if queue created with concurrency `0` (#714) -- Doc fixes (#542 #596 #615 #628 #631 #690) +- Fix unneeded iteration in `queue.resume()` (#758) +- Guard against timer mocking overriding `setImmediate` (#609 #611) +- Miscellaneous doc fixes (#542 #596 #615 #628 #631 #690 #729) - Use single noop function internally (#546) - Optimize internal `_each`, `_map` and `_keys` functions. diff --git a/support/sync-package-managers.js b/support/sync-package-managers.js index 30cb7c2..5b26119 100755 --- a/support/sync-package-managers.js +++ b/support/sync-package-managers.js @@ -43,7 +43,7 @@ var componentSpecific = { }; var componentInclude = ['name', 'description', 'version', 'keywords', - 'license']; + 'license', 'main']; var bowerJson = _.merge({}, _.pick(packageJson, bowerInclude), bowerSpecific); var componentJson = _.merge({}, _.pick(packageJson, componentInclude), componentSpecific); -- cgit v1.2.1 From cfa81645c9cb4011b23d1d1a445ad855762568e0 Mon Sep 17 00:00:00 2001 From: Alexander Early Date: Wed, 20 May 2015 16:40:25 -0700 Subject: v1.0.0 --- bower.json | 10 ++++++---- component.json | 3 ++- package.json | 7 +++---- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/bower.json b/bower.json index 1817688..9e4156d 100644 --- a/bower.json +++ b/bower.json @@ -1,7 +1,7 @@ { "name": "async", "description": "Higher-order functions and common patterns for asynchronous code", - "version": "0.9.2", + "version": "1.0.0", "main": "lib/async.js", "keywords": [ "async", @@ -15,10 +15,12 @@ "url": "https://github.com/caolan/async.git" }, "devDependencies": { + "benchmark": "~1.0.0", + "jshint": "~2.7.0", + "lodash": ">=2.4.1", + "mkdirp": "~0.5.1", "nodeunit": ">0.0.0", - "uglify-js": "1.2.x", - "nodelint": ">0.0.0", - "lodash": ">=2.4.1" + "uglify-js": "1.2.x" }, "moduleType": [ "amd", diff --git a/component.json b/component.json index 5003a7c..c876b0a 100644 --- a/component.json +++ b/component.json @@ -1,7 +1,7 @@ { "name": "async", "description": "Higher-order functions and common patterns for asynchronous code", - "version": "0.9.2", + "version": "1.0.0", "keywords": [ "async", "callback", @@ -9,6 +9,7 @@ "module" ], "license": "MIT", + "main": "lib/async.js", "repository": "caolan/async", "scripts": [ "lib/async.js" diff --git a/package.json b/package.json index ec89faf..14b05c6 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "description": "Higher-order functions and common patterns for asynchronous code", "main": "lib/async.js", "author": "Caolan McMahon", - "version": "0.9.2", + "version": "1.0.0", "keywords": [ "async", "callback", @@ -51,8 +51,7 @@ "node_modules", "bower_components", "test", - "tests", - "perf" + "tests" ] } -} +} \ No newline at end of file -- cgit v1.2.1 From a307b191ec50ecf22e3da157297b3628049c2068 Mon Sep 17 00:00:00 2001 From: Suguru Motegi Date: Thu, 21 May 2015 12:27:19 +0900 Subject: fix(benchmark): fix to enable to use second argument version --- perf/benchmark.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/perf/benchmark.js b/perf/benchmark.js index 903d04a..f78ae04 100755 --- a/perf/benchmark.js +++ b/perf/benchmark.js @@ -99,7 +99,7 @@ function createSuite(suiteConfig) { function addBench(version, versionName) { var name = suiteConfig.name + " " + versionName; suite.add(name, function (deferred) { - suiteConfig.fn(versions[0], function () { + suiteConfig.fn(version, function () { deferred.resolve(); }); }, _.extend({ -- cgit v1.2.1 From c8ed5ee14699f95c80a556b2fc667a96036e7bf1 Mon Sep 17 00:00:00 2001 From: Alexander Early Date: Wed, 20 May 2015 21:18:59 -0700 Subject: improved benchmark conclusions [ci skip] --- perf/benchmark.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/perf/benchmark.js b/perf/benchmark.js index f78ae04..d69243a 100755 --- a/perf/benchmark.js +++ b/perf/benchmark.js @@ -55,6 +55,9 @@ async.eachSeries(versionNames, cloneVersion, function (err) { var totalTime0 = Math.round(totalTime[version0]); var totalTime1 = Math.round(totalTime[version1]); + var wins0 = Math.round(wins[version0]); + var wins1 = Math.round(wins[version1]); + if ( Math.abs((totalTime0 / totalTime1) - 1) < 0.01) { // if < 1% difference, we're likely within the margins of error console.log("Both versions are about equal " + @@ -66,6 +69,17 @@ async.eachSeries(versionNames, cloneVersion, function (err) { console.log(version1 + " faster overall " + "(" + totalTime1 + "ms total vs. " + totalTime0 + "ms total)"); } + + if (wins0 > wins1) { + console.log(version0 + " won more benchmarks " + + "(" + wins0 + " vs. " + wins1 + ")"); + } else if (wins1 > wins0) { + console.log(version1 + " won more benchmarks " + + "(" + wins1 + " vs. " + wins0 + ")"); + } else { + console.log("Both versions won the same number of benchmarks " + + "(" + wins0 + " vs. " + wins1 + ")"); + } }); }); -- cgit v1.2.1 From a2c8cc8648021c9bf7b43c13ed196788491eff87 Mon Sep 17 00:00:00 2001 From: Alexander Early Date: Wed, 20 May 2015 22:29:37 -0700 Subject: added additional detectSeries test, clarified docs. Closes #534 --- README.md | 11 +++++++---- test/test-async.js | 10 ++++++++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 9051406..01ab163 100644 --- a/README.md +++ b/README.md @@ -552,11 +552,11 @@ __Arguments__ * `arr` - An array to iterate over. * `iterator(item, callback)` - A truth test to apply to each item in `arr`. The iterator is passed a `callback(truthValue)` which must be called with a - boolean argument once it has completed. + boolean argument once it has completed. **Note: this callback does not take an error as its first argument.** * `callback(result)` - A callback which is called as soon as any iterator returns `true`, or after all the `iterator` functions have finished. Result will be the first item in the array that passes the truth test (iterator) or the - value `undefined` if none passed. + value `undefined` if none passed. **Note: this callback does not take an error as its first argument.** __Example__ @@ -644,12 +644,13 @@ __Arguments__ * `arr` - An array to iterate over. * `iterator(item, callback)` - A truth test to apply to each item in the array - in parallel. The iterator is passed a callback(truthValue) which must be + in parallel. The iterator is passed a `callback(truthValue)`` which must be called with a boolean argument once it has completed. * `callback(result)` - A callback which is called as soon as any iterator returns `true`, or after all the iterator functions have finished. Result will be either `true` or `false` depending on the values of the async tests. + **Note: the callbacks do not take an error as their first argument.** __Example__ ```js @@ -674,12 +675,14 @@ __Arguments__ * `arr` - An array to iterate over. * `iterator(item, callback)` - A truth test to apply to each item in the array - in parallel. The iterator is passed a callback(truthValue) which must be + in parallel. The iterator is passed a `callback(truthValue)` which must be called with a boolean argument once it has completed. * `callback(result)` - A callback which is called after all the `iterator` functions have finished. Result will be either `true` or `false` depending on the values of the async tests. + **Note: the callbacks do not take an error as their first argument.** + __Example__ ```js diff --git a/test/test-async.js b/test/test-async.js index 64c33f9..fe927de 100755 --- a/test/test-async.js +++ b/test/test-async.js @@ -1895,6 +1895,16 @@ exports['detectSeries - multiple matches'] = function(test){ }, 200); }; +exports['detectSeries - ensure stop'] = function (test) { + async.detectSeries([1, 2, 3, 4, 5], function (num, cb) { + if (num > 3) throw new Error("detectSeries did not stop iterating"); + cb(num === 3); + }, function (result) { + test.equals(result, 3); + test.done(); + }); +}; + exports['sortBy'] = function(test){ async.sortBy([{a:1},{a:15},{a:6}], function(x, callback){ setTimeout(function(){callback(null, x.a);}, 0); -- cgit v1.2.1 From 777439ff6a2a3b6a28d3cb0850246a87f3773445 Mon Sep 17 00:00:00 2001 From: Alexander Early Date: Wed, 20 May 2015 22:42:08 -0700 Subject: added test case for #489, #259 --- test/test-async.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/test/test-async.js b/test/test-async.js index fe927de..108d8b8 100755 --- a/test/test-async.js +++ b/test/test-async.js @@ -3391,6 +3391,25 @@ exports['queue empty'] = function(test) { q.push([]); }; +exports['queue saturated'] = function (test) { + var saturatedCalled = false; + var q = async.queue(function(task, cb) { + async.setImmediate(cb); + }, 2); + + q.saturated = function () { + saturatedCalled = true; + }; + q.drain = function () { + test.ok(saturatedCalled, "saturated not called"); + test.done(); + }; + + setTimeout(function () { + q.push(['foo', 'bar', 'baz', 'moo']); + }, 10); +}; + exports['queue started'] = function(test) { var calls = []; -- cgit v1.2.1 From e968c02b641f65b9ae458d2bc30a4668ef6fd82c Mon Sep 17 00:00:00 2001 From: Alexander Early Date: Wed, 20 May 2015 23:19:04 -0700 Subject: pass err as null for consistency. Closes #491 --- lib/async.js | 32 ++++++++++++++++---------------- test/test-async.js | 51 ++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 54 insertions(+), 29 deletions(-) diff --git a/lib/async.js b/lib/async.js index 1e76ee8..439ecc9 100644 --- a/lib/async.js +++ b/lib/async.js @@ -150,7 +150,7 @@ async.each = function (arr, iterator, callback) { callback = callback || noop; if (!arr.length) { - return callback(); + return callback(null); } var completed = 0; _each(arr, function (x) { @@ -164,7 +164,7 @@ else { completed += 1; if (completed >= arr.length) { - callback(); + callback(null); } } } @@ -174,7 +174,7 @@ async.eachSeries = function (arr, iterator, callback) { callback = callback || noop; if (!arr.length) { - return callback(); + return callback(null); } var completed = 0; var iterate = function () { @@ -186,7 +186,7 @@ else { completed += 1; if (completed >= arr.length) { - callback(); + callback(null); } else { iterate(); @@ -210,7 +210,7 @@ return function (arr, iterator, callback) { callback = callback || noop; if (!arr.length || limit <= 0) { - return callback(); + return callback(null); } var completed = 0; var started = 0; @@ -218,7 +218,7 @@ (function replenish () { if (completed >= arr.length) { - return callback(); + return callback(null); } while (running < limit && started < arr.length) { @@ -233,7 +233,7 @@ completed += 1; running -= 1; if (completed >= arr.length) { - callback(); + callback(null); } else { replenish(); @@ -252,7 +252,7 @@ var size = object.length || _keys(object).length; var completed = 0; if (!size) { - return callback(); + return callback(null); } _forEachOf(object, function (value, key) { iterator(object[key], key, function (err) { @@ -318,7 +318,7 @@ var keys = _keys(obj); var size = keys.length; if (!size || limit <= 0) { - return callback(); + return callback(null); } var completed = 0; var started = 0; @@ -342,7 +342,7 @@ completed += 1; running -= 1; if (completed >= size) { - callback(); + callback(null); } else { replenish(); @@ -416,7 +416,7 @@ callback(err); }); }, function (err) { - callback(err, memo); + callback(err || null, memo); }); }; // inject alias @@ -563,7 +563,7 @@ var keys = _keys(tasks); var remainingTasks = keys.length; if (!remainingTasks) { - return callback(); + return callback(null); } var results = {}; @@ -836,7 +836,7 @@ }); } else { - callback(); + callback(null); } }; @@ -850,7 +850,7 @@ async.doWhilst(iterator, test, callback); } else { - callback(); + callback(null); } }); }; @@ -865,7 +865,7 @@ }); } else { - callback(); + callback(null); } }; @@ -879,7 +879,7 @@ async.doUntil(iterator, test, callback); } else { - callback(); + callback(null); } }); }; diff --git a/test/test-async.js b/test/test-async.js index 108d8b8..13c3739 100755 --- a/test/test-async.js +++ b/test/test-async.js @@ -99,7 +99,7 @@ exports['forever'] = function (test) { }; exports['applyEach'] = function (test) { - test.expect(4); + test.expect(5); var call_order = []; var one = function (val, cb) { test.equal(val, 5); @@ -123,13 +123,14 @@ exports['applyEach'] = function (test) { }, 150); }; async.applyEach([one, two, three], 5, function (err) { + test.ok(err === null, err + " passed instead of 'null'"); test.same(call_order, ['two', 'one', 'three']); test.done(); }); }; exports['applyEachSeries'] = function (test) { - test.expect(4); + test.expect(5); var call_order = []; var one = function (val, cb) { test.equal(val, 5); @@ -153,6 +154,7 @@ exports['applyEachSeries'] = function (test) { }, 150); }; async.applyEachSeries([one, two, three], 5, function (err) { + test.ok(err === null, err + " passed instead of 'null'"); test.same(call_order, ['one', 'two', 'three']); test.done(); }); @@ -189,7 +191,7 @@ exports['applyEach partial application'] = function (test) { }; exports['compose'] = function (test) { - test.expect(4); + test.expect(5); var add2 = function (n, cb) { test.equal(n, 3); setTimeout(function () { @@ -213,6 +215,7 @@ exports['compose'] = function (test) { if (err) { return test.done(err); } + test.ok(err === null, err + " passed instead of 'null'"); test.equal(result, 16); test.done(); }); @@ -276,7 +279,7 @@ exports['compose binding'] = function (test) { }; exports['seq'] = function (test) { - test.expect(4); + test.expect(5); var add2 = function (n, cb) { test.equal(n, 3); setTimeout(function () { @@ -300,6 +303,7 @@ exports['seq'] = function (test) { if (err) { return test.done(err); } + test.ok(err === null, err + " passed instead of 'null'"); test.equal(result, 16); test.done(); }); @@ -398,6 +402,7 @@ exports['auto'] = function(test){ }] }, function(err){ + test.ok(err === null, err + " passed instead of 'null'"); test.same(callOrder, ['task2','task6','task3','task5','task1','task4']); test.done(); }); @@ -471,6 +476,7 @@ exports['auto results'] = function(test){ exports['auto empty object'] = function(test){ async.auto({}, function(err){ + test.ok(err === null, err + " passed instead of 'null'"); test.done(); }); }; @@ -633,6 +639,7 @@ exports['retry when attempt succeeds'] = function(test) { else callback(true); // respond with error } async.retry(fn, function(err, result){ + test.ok(err === null, err + " passed instead of 'null'"); test.equal(callCount, 3, 'did not retry the correct number of times'); test.equal(result, expectedResult, 'did not return the expected result'); test.done(); @@ -678,7 +685,7 @@ exports['retry as an embedded task'] = function(test) { }; exports['waterfall'] = function(test){ - test.expect(6); + test.expect(7); var call_order = []; async.waterfall([ function(callback){ @@ -704,6 +711,7 @@ exports['waterfall'] = function(test){ callback(null, 'test'); } ], function(err){ + test.ok(err === null, err + " passed instead of 'null'"); test.done(); }); }; @@ -843,7 +851,7 @@ exports['parallel'] = function(test){ } ], function(err, results){ - test.equals(err, null); + test.ok(err === null, err + " passed instead of 'null'"); test.same(call_order, [3,1,2]); test.same(results, [1,2,[3,3]]); test.done(); @@ -852,7 +860,7 @@ exports['parallel'] = function(test){ exports['parallel empty array'] = function(test){ async.parallel([], function(err, results){ - test.equals(err, null); + test.ok(err === null, err + " passed instead of 'null'"); test.same(results, []); test.done(); }); @@ -918,7 +926,7 @@ exports['parallel limit'] = function(test){ ], 2, function(err, results){ - test.equals(err, null); + test.ok(err === null, err + " passed instead of 'null'"); test.same(call_order, [1,3,2]); test.same(results, [1,2,[3,3]]); test.done(); @@ -927,7 +935,7 @@ exports['parallel limit'] = function(test){ exports['parallel limit empty array'] = function(test){ async.parallelLimit([], 2, function(err, results){ - test.equals(err, null); + test.ok(err === null, err + " passed instead of 'null'"); test.same(results, []); test.done(); }); @@ -1020,7 +1028,7 @@ exports['series'] = function(test){ } ], function(err, results){ - test.equals(err, null); + test.ok(err === null, err + " passed instead of 'null'"); test.same(results, [1,2,[3,3]]); test.same(call_order, [1,2,3]); test.done(); @@ -1159,6 +1167,7 @@ exports['iterator.next'] = function(test){ exports['each'] = function(test){ var args = []; async.each([1,3,2], eachIterator.bind(this, args), function(err){ + test.ok(err === null, err + " passed instead of 'null'"); test.same(args, [1,2,3]); test.done(); }); @@ -1209,6 +1218,7 @@ exports['forEach alias'] = function (test) { exports['forEachOf'] = function(test){ var args = []; async.forEachOf({ a: 1, b: 2 }, forEachOfIterator.bind(this, args), function(err){ + test.ok(err === null, err + " passed instead of 'null'"); test.same(args, ["a", 1, "b", 2]); test.done(); }); @@ -1250,6 +1260,7 @@ exports['forEachOf with array'] = function(test){ exports['eachSeries'] = function(test){ var args = []; async.eachSeries([1,3,2], eachIterator.bind(this, args), function(err){ + test.ok(err === null, err + " passed instead of 'null'"); test.same(args, [1,3,2]); test.done(); }); @@ -1293,6 +1304,7 @@ exports['eachLimit'] = function(test){ callback(); }, x*5); }, function(err){ + test.ok(err === null, err + " passed instead of 'null'"); test.same(args, arr); test.done(); }); @@ -1379,6 +1391,7 @@ exports['forEachSeries alias'] = function (test) { exports['forEachOfSeries'] = function(test){ var args = []; async.forEachOfSeries({ a: 1, b: 2 }, forEachOfIterator.bind(this, args), function(err){ + test.ok(err === null, err + " passed instead of 'null'"); test.same(args, [ "a", 1, "b", 2 ]); test.done(); }); @@ -1434,6 +1447,7 @@ exports['forEachOfLimit'] = function(test){ callback(); }, value * 5); }, function(err){ + test.ok(err === null, err + " passed instead of 'null'"); test.same(args, [ 1, "a", 2, "b", 3, "c", 4, "d" ]); test.done(); }); @@ -1521,6 +1535,7 @@ exports['forEachOfLimit with array'] = function(test){ exports['map'] = function(test){ var call_order = []; async.map([1,3,2], mapIterator.bind(this, call_order), function(err, results){ + test.ok(err === null, err + " passed instead of 'null'"); test.same(call_order, [1,2,3]); test.same(results, [2,6,4]); test.done(); @@ -1564,6 +1579,7 @@ exports['map error'] = function(test){ exports['mapSeries'] = function(test){ var call_order = []; async.mapSeries([1,3,2], mapIterator.bind(this, call_order), function(err, results){ + test.ok(err === null, err + " passed instead of 'null'"); test.same(call_order, [1,3,2]); test.same(results, [2,6,4]); test.done(); @@ -1584,6 +1600,7 @@ exports['mapSeries error'] = function(test){ exports['mapLimit'] = function(test){ var call_order = []; async.mapLimit([2,4,3], 2, mapIterator.bind(this, call_order), function(err, results){ + test.ok(err === null, err + " passed instead of 'null'"); test.same(call_order, [2,4,3]); test.same(results, [4,8,6]); test.done(); @@ -1655,6 +1672,7 @@ exports['reduce'] = function(test){ call_order.push(x); callback(null, a + x); }, function(err, result){ + test.ok(err === null, err + " passed instead of 'null'"); test.equals(result, 6); test.same(call_order, [1,2,3]); test.done(); @@ -1909,6 +1927,7 @@ exports['sortBy'] = function(test){ async.sortBy([{a:1},{a:15},{a:6}], function(x, callback){ setTimeout(function(){callback(null, x.a);}, 0); }, function(err, result){ + test.ok(err === null, err + " passed instead of 'null'"); test.same(result, [{a:1},{a:6},{a:15}]); test.done(); }); @@ -2008,6 +2027,7 @@ exports['times'] = function(test) { async.times(5, function(n, next) { next(null, n); }, function(err, results) { + test.ok(err === null, err + " passed instead of 'null'"); test.same(results, [0,1,2,3,4]); test.done(); }); @@ -2151,7 +2171,7 @@ exports['concat'] = function(test){ async.concat([1,3,2], iterator, function(err, results){ test.same(results, [1,2,1,3,2,1]); test.same(call_order, [1,2,3]); - test.ok(!err); + test.ok(err === null, err + " passed instead of 'null'"); test.done(); }); }; @@ -2182,7 +2202,7 @@ exports['concatSeries'] = function(test){ async.concatSeries([1,3,2], iterator, function(err, results){ test.same(results, [1,3,2,1,2,1]); test.same(call_order, [1,3,2]); - test.ok(!err); + test.ok(err === null, err + " passed instead of 'null'"); test.done(); }); }; @@ -2202,6 +2222,7 @@ exports['until'] = function (test) { cb(); }, function (err) { + test.ok(err === null, err + " passed instead of 'null'"); test.same(call_order, [ ['test', 0], ['iterator', 0], ['test', 1], @@ -2230,6 +2251,7 @@ exports['doUntil'] = function (test) { return (count == 5); }, function (err) { + test.ok(err === null, err + " passed instead of 'null'"); test.same(call_order, [ ['iterator', 0], ['test', 1], ['iterator', 1], ['test', 2], @@ -2285,6 +2307,7 @@ exports['whilst'] = function (test) { cb(); }, function (err) { + test.ok(err === null, err + " passed instead of 'null'"); test.same(call_order, [ ['test', 0], ['iterator', 0], ['test', 1], @@ -2314,6 +2337,7 @@ exports['doWhilst'] = function (test) { return (count < 5); }, function (err) { + test.ok(err === null, err + " passed instead of 'null'"); test.same(call_order, [ ['iterator', 0], ['test', 1], ['iterator', 1], ['test', 2], @@ -3087,7 +3111,7 @@ exports['cargo drain twice'] = function (test) { }; exports['memoize'] = function (test) { - test.expect(4); + test.expect(5); var call_order = []; var fn = function (arg1, arg2, callback) { @@ -3099,6 +3123,7 @@ exports['memoize'] = function (test) { var fn2 = async.memoize(fn); fn2(1, 2, function (err, result) { + test.ok(err === null, err + " passed instead of 'null'"); test.equal(result, 3); fn2(1, 2, function (err, result) { test.equal(result, 3); -- cgit v1.2.1 From 651232ed9f4779b6cecf58cbeb1c6b99e56f16b5 Mon Sep 17 00:00:00 2001 From: Alexander Early Date: Wed, 20 May 2015 23:33:57 -0700 Subject: added tests for #508 and #512 --- test/test-async.js | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/test/test-async.js b/test/test-async.js index 13c3739..7c325c8 100755 --- a/test/test-async.js +++ b/test/test-async.js @@ -2790,6 +2790,10 @@ exports['queue pause with concurrency'] = function(test) { test.equal(q.paused, false); }, resume_timeout); + setTimeout(function () { + test.equal(q.running(), 2); + }, resume_timeout + 10); + setTimeout(function () { test.same(call_order, [ 'process 1', 'timeout 100', @@ -2803,6 +2807,30 @@ exports['queue pause with concurrency'] = function(test) { }, 800); }; +exports['queue start paused'] = function (test) { + var q = async.queue(function (task, callback) { + setTimeout(function () { + callback(); + }, 10); + }, 2); + q.pause(); + + q.push([1, 2, 3]); + + setTimeout(function () { + q.resume(); + }, 10); + + setTimeout(function () { + test.equal(q.running(), 2); + q.resume(); + }, 15); + + q.drain = function () { + test.done(); + }; +}; + exports['queue kill'] = function (test) { var q = async.queue(function (task, callback) { setTimeout(function () { -- cgit v1.2.1 From 78a77f4c8306c2783d33309220451a15fde1ea02 Mon Sep 17 00:00:00 2001 From: Alexander Early Date: Wed, 20 May 2015 23:43:03 -0700 Subject: clarify queue docs. closes #589 and #599 --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 01ab163..4e0b9fc 100644 --- a/README.md +++ b/README.md @@ -1128,7 +1128,7 @@ The same as [`applyEach`](#applyEach) only the functions are applied in series. --------------------------------------- -### queue(worker, concurrency) +### queue(worker, [concurrency]) Creates a `queue` object with the specified `concurrency`. Tasks added to the `queue` are processed in parallel (up to the `concurrency` limit). If all @@ -1139,9 +1139,9 @@ __Arguments__ * `worker(task, callback)` - An asynchronous function for processing a queued task, which must call its `callback(err)` argument when finished, with an - optional `error` as an argument. + optional `error` as an argument. If you want to handle errors from an individual task, pass a callback to `q.push()`. * `concurrency` - An `integer` for determining how many `worker` functions should be - run in parallel. + run in parallel. If omitted, the concurrency defaults to `1`. If the concurrency is `0`, an error is thrown. __Queue objects__ -- cgit v1.2.1 From 0da3ebbaeb1ac79dac4c156a96770005313cd667 Mon Sep 17 00:00:00 2001 From: Alexander Early Date: Thu, 21 May 2015 21:53:07 -0700 Subject: added yargs to benchmark runner, and grep options --- package.json | 5 +++-- perf/benchmark.js | 53 ++++++++++++++++++++++++++++++++--------------------- 2 files changed, 35 insertions(+), 23 deletions(-) diff --git a/package.json b/package.json index 14b05c6..8e5d203 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,8 @@ "lodash": ">=2.4.1", "mkdirp": "~0.5.1", "nodeunit": ">0.0.0", - "uglify-js": "1.2.x" + "uglify-js": "1.2.x", + "yargs": "~3.9.1" }, "jam": { "main": "lib/async.js", @@ -54,4 +55,4 @@ "tests" ] } -} \ No newline at end of file +} diff --git a/perf/benchmark.js b/perf/benchmark.js index d69243a..6ae3d04 100755 --- a/perf/benchmark.js +++ b/perf/benchmark.js @@ -1,24 +1,5 @@ #!/usr/bin/env node -/** - * Compare the performance of any two tagged versions of async. Can also - * compare any tag with what is in the current working directory. - * - * Usage: - * - * perf/benchmark.js 0.9.2 0.9.0 - * - * Compare version 0.9.0 with version 0.9.2 - * - * perf/benchmark.js 0.6.0 - * - * Compare version 0.6.0 with the current working version - * - * perf/benchmark.js - * - * Compare the current working version with the latest tagged version. - */ - var _ = require("lodash"); var Benchmark = require("benchmark"); var benchOptions = {defer: true, minSamples: 1, maxTime: 2}; @@ -29,8 +10,28 @@ var mkdirp = require("mkdirp"); var async = require("../"); var suiteConfigs = require("./suites"); -var version0 = process.argv[2] || require("../package.json").version; -var version1 = process.argv[3] || "current"; +var args = require("yargs") + .usage("Usage: $0 [options] [tag1] [tag2]") + .describe("g", "run only benchmarks whose names match this regex") + .alias("g", "grep") + .default("g", ".*") + .describe("i", "skip benchmarks whose names match this regex") + .alias("g", "reject") + .default("i", "^$") + .help('h') + .alias('h', 'help') + .example('$0 0.9.2 0.9.0', 'Compare v0.9.2 with v0.9.0') + .example('$0 0.9.2', 'Compare v0.9.2 with the current working version') + .example('$0', 'Compare the latest tag with the current working version') + .example('$0 -g each', 'only run the each(), eachLimit() and eachSeries() benchmarks') + .example('') + .argv; + +var grep = new RegExp(args.g, "i"); +var reject = new RegExp(args.i, "i"); + +var version0 = args._[0] || require("../package.json").version; +var version1 = args._[1] || "current"; var versionNames = [version0, version1]; var versions; var wins = {}; @@ -49,6 +50,8 @@ async.eachSeries(versionNames, cloneVersion, function (err) { .map(setDefaultOptions) .reduce(handleMultipleArgs, []) .map(setName) + .filter(matchesGrep) + .filter(doesNotMatch) .map(createSuite); async.eachSeries(suites, runSuite, function () { @@ -106,6 +109,14 @@ function setName(suiteConfig) { return suiteConfig; } +function matchesGrep(suiteConfig) { + return !!grep.exec(suiteConfig.name); +} + +function doesNotMatch(suiteConfig) { + return !reject.exec(suiteConfig.name); +} + function createSuite(suiteConfig) { var suite = new Benchmark.Suite(); var args = suiteConfig.args; -- cgit v1.2.1 From 5b6f080710fb4dbb6ef753d8680a2396ce73f8f8 Mon Sep 17 00:00:00 2001 From: Alexander Early Date: Thu, 21 May 2015 22:11:15 -0700 Subject: added benchmarks for map and eachOf --- perf/suites.js | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/perf/suites.js b/perf/suites.js index 2e1beaa..6504ebc 100644 --- a/perf/suites.js +++ b/perf/suites.js @@ -10,7 +10,7 @@ module.exports = [ tasks = Array(count); }, fn: function (async, done) { - async.each(Array(10), function (num, cb) { + async.each(tasks, function (num, cb) { async.setImmediate(cb); }, done); } @@ -39,6 +39,80 @@ module.exports = [ }, done); } }, + { + name: "map", + // args lists are passed to the setup function + args: [[10], [300], [10000]], + setup: function(count) { + tasks = Array(count); + }, + fn: function (async, done) { + async.map(Array(10), function (num, cb) { + async.setImmediate(cb); + }, done); + } + }, + { + name: "mapSeries", + args: [[10], [300], [10000]], + setup: function(count) { + tasks = Array(count); + }, + fn: function (async, done) { + async.mapSeries(tasks, function (num, cb) { + async.setImmediate(cb); + }, done); + } + }, + { + name: "mapLimit", + args: [[10], [300], [10000]], + setup: function(count) { + tasks = Array(count); + }, + fn: function (async, done) { + async.mapLimit(tasks, 4, function (num, cb) { + async.setImmediate(cb); + }, done); + } + }, + { + name: "eachOf", + // args lists are passed to the setup function + args: [[10], [300], [10000]], + setup: function(count) { + tasks = _.range(count); + }, + fn: function (async, done) { + async.eachOf(tasks, function (num, i, cb) { + async.setImmediate(cb); + }, done); + } + }, + { + name: "eachOfSeries", + args: [[10], [300], [10000]], + setup: function(count) { + tasks = _.range(count); + }, + fn: function (async, done) { + async.eachOfSeries(tasks, function (num, i, cb) { + async.setImmediate(cb); + }, done); + } + }, + { + name: "eachOfLimit", + args: [[10], [300], [10000]], + setup: function(count) { + tasks = _.range(count); + }, + fn: function (async, done) { + async.eachOfLimit(tasks, 4, function (num, i, cb) { + async.setImmediate(cb); + }, done); + } + }, { name: "parallel", args: [[10], [100], [1000]], -- cgit v1.2.1 From 3a1aff02096b12c0ed8669c3349f1659d96b4e5b Mon Sep 17 00:00:00 2001 From: Alexander Early Date: Thu, 21 May 2015 23:15:47 -0700 Subject: split waterfall tests into a group --- test/test-async.js | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/test/test-async.js b/test/test-async.js index 7c325c8..1cb860e 100755 --- a/test/test-async.js +++ b/test/test-async.js @@ -684,7 +684,9 @@ exports['retry as an embedded task'] = function(test) { }); }; -exports['waterfall'] = function(test){ +exports['waterfall'] = { + +'basic': function(test){ test.expect(7); var call_order = []; async.waterfall([ @@ -714,29 +716,29 @@ exports['waterfall'] = function(test){ test.ok(err === null, err + " passed instead of 'null'"); test.done(); }); -}; +}, -exports['waterfall empty array'] = function(test){ +'empty array': function(test){ async.waterfall([], function(err){ test.done(); }); -}; +}, -exports['waterfall non-array'] = function(test){ +'non-array': function(test){ async.waterfall({}, function(err){ test.equals(err.message, 'First argument to waterfall must be an array of functions'); test.done(); }); -}; +}, -exports['waterfall no callback'] = function(test){ +'no callback': function(test){ async.waterfall([ function(callback){callback();}, function(callback){callback(); test.done();} ]); -}; +}, -exports['waterfall async'] = function(test){ +'async': function(test){ var call_order = []; async.waterfall([ function(callback){ @@ -753,9 +755,9 @@ exports['waterfall async'] = function(test){ test.done(); } ]); -}; +}, -exports['waterfall error'] = function(test){ +'error': function(test){ test.expect(1); async.waterfall([ function(callback){ @@ -769,9 +771,9 @@ exports['waterfall error'] = function(test){ test.equals(err, 'error'); }); setTimeout(test.done, 50); -}; +}, -exports['waterfall multiple callback calls'] = function(test){ +'multiple callback calls': function(test){ var call_order = []; var arr = [ function(callback){ @@ -798,9 +800,9 @@ exports['waterfall multiple callback calls'] = function(test){ } ]; async.waterfall(arr); -}; +}, -exports['waterfall call in another context'] = function(test) { +'call in another context': function(test) { if (typeof process === 'undefined') { // node only test test.done(); @@ -825,8 +827,9 @@ exports['waterfall call in another context'] = function(test) { }).toString() + "())"; vm.runInNewContext(fn, sandbox); -}; +} +}; exports['parallel'] = function(test){ var call_order = []; -- cgit v1.2.1 From 7be7cc6159fa03f78bb07415f547f693cc61f3e8 Mon Sep 17 00:00:00 2001 From: Alexander Early Date: Thu, 21 May 2015 23:45:54 -0700 Subject: added deferral benchmarks --- perf/suites.js | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/perf/suites.js b/perf/suites.js index 6504ebc..b735a5f 100644 --- a/perf/suites.js +++ b/perf/suites.js @@ -172,6 +172,42 @@ module.exports = [ setImmediate(callback); } } + }, + { + name: "defer none", + fn: function (async, done) { + done(); + } + }, + { + name: "defer nextTick", + fn: function (async, done) { + process.nextTick(done); + } + }, + { + name: "defer setImmediate", + fn: function (async, done) { + setImmediate(done); + } + }, + { + name: "defer async.nextTick", + fn: function (async, done) { + async.nextTick(done); + } + }, + { + name: "defer async.setImmediate", + fn: function (async, done) { + async.setImmediate(done); + } + }, + { + name: "defer setTimeout", + fn: function (async, done) { + setTimeout(done, 0); + } } ]; -- cgit v1.2.1 From eeb33e687ba6e87f89f0452005571499859c6056 Mon Sep 17 00:00:00 2001 From: Alexander Early Date: Thu, 21 May 2015 23:59:41 -0700 Subject: update benchmark to 2.0.0-pre for more accurate tests --- package.json | 2 +- perf/suites.js | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/package.json b/package.json index 8e5d203..6e7282b 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ }, "license": "MIT", "devDependencies": { - "benchmark": "~1.0.0", + "benchmark": "bestiejs/benchmark.js", "jshint": "~2.7.0", "lodash": ">=2.4.1", "mkdirp": "~0.5.1", diff --git a/perf/suites.js b/perf/suites.js index b735a5f..6b00b25 100644 --- a/perf/suites.js +++ b/perf/suites.js @@ -173,12 +173,6 @@ module.exports = [ } } }, - { - name: "defer none", - fn: function (async, done) { - done(); - } - }, { name: "defer nextTick", fn: function (async, done) { -- cgit v1.2.1 From a44d11ca95304dca673e6487050e630c2b2d87ee Mon Sep 17 00:00:00 2001 From: Alexander Early Date: Fri, 22 May 2015 00:34:00 -0700 Subject: dezalgo'd benchmarks, use toPrecision in reporting --- perf/benchmark.js | 10 +++++----- perf/suites.js | 20 +++++++++++--------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/perf/benchmark.js b/perf/benchmark.js index 6ae3d04..da59216 100755 --- a/perf/benchmark.js +++ b/perf/benchmark.js @@ -55,11 +55,11 @@ async.eachSeries(versionNames, cloneVersion, function (err) { .map(createSuite); async.eachSeries(suites, runSuite, function () { - var totalTime0 = Math.round(totalTime[version0]); - var totalTime1 = Math.round(totalTime[version1]); + var totalTime0 = +totalTime[version0].toPrecision(3); + var totalTime1 = +totalTime[version1].toPrecision(3); - var wins0 = Math.round(wins[version0]); - var wins1 = Math.round(wins[version1]); + var wins0 = wins[version0]; + var wins1 = wins[version1]; if ( Math.abs((totalTime0 / totalTime1) - 1) < 0.01) { // if < 1% difference, we're likely within the margins of error @@ -139,7 +139,7 @@ function createSuite(suiteConfig) { return suite.on('cycle', function(event) { var mean = event.target.stats.mean * 1000; - console.log(event.target + ", " + mean.toFixed(1) + "ms per sample"); + console.log(event.target + ", " + (+mean.toPrecision(2)) + "ms per run"); var version = event.target.options.versionName; totalTime[version] += mean; }) diff --git a/perf/suites.js b/perf/suites.js index 6b00b25..9a36db5 100644 --- a/perf/suites.js +++ b/perf/suites.js @@ -7,7 +7,7 @@ module.exports = [ // args lists are passed to the setup function args: [[10], [300], [10000]], setup: function(count) { - tasks = Array(count); + tasks = _.range(count); }, fn: function (async, done) { async.each(tasks, function (num, cb) { @@ -19,7 +19,7 @@ module.exports = [ name: "eachSeries", args: [[10], [300], [10000]], setup: function(count) { - tasks = Array(count); + tasks = _.range(count); }, fn: function (async, done) { async.eachSeries(tasks, function (num, cb) { @@ -31,7 +31,7 @@ module.exports = [ name: "eachLimit", args: [[10], [300], [10000]], setup: function(count) { - tasks = Array(count); + tasks = _.range(count); }, fn: function (async, done) { async.eachLimit(tasks, 4, function (num, cb) { @@ -44,10 +44,10 @@ module.exports = [ // args lists are passed to the setup function args: [[10], [300], [10000]], setup: function(count) { - tasks = Array(count); + tasks = _.range(count); }, fn: function (async, done) { - async.map(Array(10), function (num, cb) { + async.map(tasks, function (num, cb) { async.setImmediate(cb); }, done); } @@ -56,7 +56,7 @@ module.exports = [ name: "mapSeries", args: [[10], [300], [10000]], setup: function(count) { - tasks = Array(count); + tasks = _.range(count); }, fn: function (async, done) { async.mapSeries(tasks, function (num, cb) { @@ -68,7 +68,7 @@ module.exports = [ name: "mapLimit", args: [[10], [300], [10000]], setup: function(count) { - tasks = Array(count); + tasks = _.range(count); }, fn: function (async, done) { async.mapLimit(tasks, 4, function (num, cb) { @@ -118,7 +118,9 @@ module.exports = [ args: [[10], [100], [1000]], setup: function (count) { tasks = _.range(count).map(function () { - return function (cb) { cb(); }; + return function (cb) { + setImmediate(cb); + }; }); }, fn: function (async, done) { @@ -130,7 +132,7 @@ module.exports = [ args: [[10], [100], [1000]], setup: function (count) { tasks = _.range(count).map(function () { - return function (cb) { cb(); }; + return function (cb) { setImmediate(cb); }; }); }, fn: function (async, done) { -- cgit v1.2.1