summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Early <alexander.early@gmail.com>2015-07-02 10:51:44 -0700
committerAlexander Early <alexander.early@gmail.com>2015-07-02 10:51:44 -0700
commit0b7a7253432435751c349cfecd7b1403289aa8fc (patch)
treebb451ac17db4e78bd75b1f1c48478bb93874612a
parentea84790dac32cfebec2dfcf57533258a42d0d335 (diff)
parent529cd5ccb3b5ad4901d725602ab47bc96e847b12 (diff)
downloadasync-0b7a7253432435751c349cfecd7b1403289aa8fc.tar.gz
Merge pull request #828 from megawac/arr-testers
Add someLimit and everyLimit
-rw-r--r--README.md28
-rw-r--r--lib/async.js63
-rw-r--r--perf/suites.js60
-rwxr-xr-xtest/test-async.js63
4 files changed, 186 insertions, 28 deletions
diff --git a/README.md b/README.md
index d50da34..e09b22b 100644
--- a/README.md
+++ b/README.md
@@ -172,8 +172,11 @@ Some functions are also available in the following forms:
* [`reduce`](#reduce), [`reduceRight`](#reduceRight)
* [`detect`](#detect), `detectSeries`
* [`sortBy`](#sortBy)
-* [`some`](#some), [`every`](#every)
-* [`concat`](#concat), `concatSeries`
+* [`some`](#some)
+* [`someLimit`](#someLimit)
+* [`every`](#every)
+* [`concat`](#concat)
+* [`concatSeries`](#concatSeries)
### Control Flow
@@ -581,6 +584,27 @@ async.some(['file1','file2','file3'], fs.exists, function(result){
---------------------------------------
+<a name="someLimit" />
+### someLimit(arr, limit iterator, callback)
+
+__Alias:__ `anyLimit`
+
+The same as [`some`](#some), only no more than `limit` `iterator`s will be simultaneously
+running at any time.
+
+__Arguments__
+
+* `arr` - An array to iterate over.
+* `limit` - The maximum number of `iterator`s to run at any time.
+* `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
+ 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.
+
+---------------------------------------
+
<a name="every" />
### every(arr, iterator, [callback])
diff --git a/lib/async.js b/lib/async.js
index f176085..e171151 100644
--- a/lib/async.js
+++ b/lib/async.js
@@ -9,6 +9,12 @@
var async = {};
function noop() {}
+ function identity(v) {
+ return v;
+ }
+ function notId(v) {
+ return !v;
+ }
// global on the server, window in the browser
var previous_async;
@@ -449,35 +455,40 @@
async.detect = doParallel(_detect);
async.detectSeries = doSeries(_detect);
+ function _createTester(eachfn, check, defaultValue) {
+ return function(arr, limit, iterator, cb) {
+ function done() {
+ if (cb) cb(defaultValue);
+ }
+ function iteratee(x, _, callback) {
+ if (!cb) return callback();
+ iterator(x, function (v) {
+ if (cb && check(v)) {
+ cb(!defaultValue);
+ cb = iterator = false;
+ }
+ callback();
+ });
+ }
+ if (arguments.length > 3) {
+ eachfn(arr, limit, iteratee, done);
+ } else {
+ cb = iterator;
+ iterator = limit;
+ eachfn(arr, iteratee, done);
+ }
+ };
+ }
+
async.any =
- async.some = function (arr, iterator, main_callback) {
- async.eachOf(arr, function (x, _, callback) {
- iterator(x, function (v) {
- if (v) {
- main_callback(true);
- main_callback = noop;
- }
- callback();
- });
- }, function () {
- main_callback(false);
- });
- };
+ async.some = _createTester(async.eachOf, identity, false);
+
+ async.someLimit = _createTester(async.eachOfLimit, identity, false);
async.all =
- async.every = function (arr, iterator, main_callback) {
- async.eachOf(arr, function (x, _, callback) {
- iterator(x, function (v) {
- if (!v) {
- main_callback(false);
- main_callback = noop;
- }
- callback();
- });
- }, function () {
- main_callback(true);
- });
- };
+ async.every = _createTester(async.eachOf, notId, true);
+
+ async.everyLimit = _createTester(async.eachOfLimit, notId, true);
async.sortBy = function (arr, iterator, callback) {
async.map(arr, function (x, callback) {
diff --git a/perf/suites.js b/perf/suites.js
index b4ea76b..42611db 100644
--- a/perf/suites.js
+++ b/perf/suites.js
@@ -180,6 +180,66 @@ module.exports = [
}
},
{
+ name: "some - no short circuit- false",
+ // args lists are passed to the setup function
+ args: [[500]],
+ setup: function(count) {
+ tasks = _.range(count);
+ },
+ fn: function (async, done) {
+ async.some(tasks, function(i, cb) {
+ async.setImmediate(function() {
+ cb(i >= 600);
+ });
+ }, done);
+ }
+ },
+ {
+ name: "some - short circuit - true",
+ // args lists are passed to the setup function
+ args: [[500]],
+ setup: function(count) {
+ tasks = _.range(count);
+ },
+ fn: function (async, done) {
+ async.some(tasks, function(i, cb) {
+ async.setImmediate(function() {
+ cb(i >= 60);
+ });
+ }, done);
+ }
+ },
+ {
+ name: "every - no short circuit- true",
+ // args lists are passed to the setup function
+ args: [[500]],
+ setup: function(count) {
+ tasks = _.range(count);
+ },
+ fn: function (async, done) {
+ async.every(tasks, function(i, cb) {
+ async.setImmediate(function() {
+ cb(i <= 600);
+ });
+ }, done);
+ }
+ },
+ {
+ name: "every - short circuit - false",
+ // args lists are passed to the setup function
+ args: [[500]],
+ setup: function(count) {
+ tasks = _.range(count);
+ },
+ fn: function (async, done) {
+ async.every(tasks, function(i, cb) {
+ async.setImmediate(function() {
+ cb(i <= 60);
+ });
+ }, done);
+ }
+ },
+ {
name: "defer nextTick",
fn: function (async, done) {
process.nextTick(done);
diff --git a/test/test-async.js b/test/test-async.js
index 25e964e..07b72e7 100755
--- a/test/test-async.js
+++ b/test/test-async.js
@@ -2236,6 +2236,69 @@ exports['some early return'] = function(test){
}, 100);
};
+exports['someLimit true'] = function(test){
+ async.someLimit([3,1,2], 2, function(x, callback){
+ setTimeout(function(){callback(x === 2);}, 0);
+ }, function(result){
+ test.equals(result, true);
+ test.done();
+ });
+};
+
+exports['someLimit false'] = function(test){
+ async.someLimit([3,1,2], 2, function(x, callback){
+ setTimeout(function(){callback(x === 10);}, 0);
+ }, function(result){
+ test.equals(result, false);
+ test.done();
+ });
+};
+
+exports['every true'] = function(test){
+ async.everyLimit([3,1,2], 1, function(x, callback){
+ setTimeout(function(){callback(x > 1);}, 0);
+ }, function(result){
+ test.equals(result, true);
+ test.done();
+ });
+};
+
+exports['everyLimit false'] = function(test){
+ async.everyLimit([3,1,2], 2, function(x, callback){
+ setTimeout(function(){callback(x === 2);}, 0);
+ }, function(result){
+ test.equals(result, false);
+ test.done();
+ });
+};
+
+exports['everyLimit short-circuit'] = function(test){
+ test.expect(2);
+ var calls = 0;
+ async.everyLimit([3,1,2], 1, function(x, callback){
+ calls++;
+ callback(x === 1);
+ }, function(result){
+ test.equals(result, false);
+ test.equals(calls, 1);
+ test.done();
+ });
+};
+
+
+exports['someLimit short-circuit'] = function(test){
+ test.expect(2);
+ var calls = 0;
+ async.someLimit([3,1,2], 1, function(x, callback){
+ calls++;
+ callback(x === 1);
+ }, function(result){
+ test.equals(result, true);
+ test.equals(calls, 2);
+ test.done();
+ });
+};
+
exports['any alias'] = function(test){
test.equals(async.any, async.some);
test.done();