Skip to content

Commit

Permalink
Merge pull request #828 from megawac/arr-testers
Browse files Browse the repository at this point in the history
Add someLimit and everyLimit
  • Loading branch information
aearly committed Jul 2, 2015
2 parents ea84790 + 529cd5c commit 0b7a725
Show file tree
Hide file tree
Showing 4 changed files with 186 additions and 28 deletions.
28 changes: 26 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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])

Expand Down
63 changes: 37 additions & 26 deletions lib/async.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down
60 changes: 60 additions & 0 deletions perf/suites.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,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) {
Expand Down
63 changes: 63 additions & 0 deletions test/test-async.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down

0 comments on commit 0b7a725

Please sign in to comment.