diff --git a/README.md b/README.md index 9cbf29e98..cd549d4b2 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,11 @@ async.map(['file1','file2','file3'], fs.stat, function(err, results){ // results is now an array of stats for each file }); -async.filter(['file1','file2','file3'], fs.exists, function(results){ +async.filter(['file1','file2','file3'], function(filePath, callback) { + fs.access(filePath, function(err) { + callback(null, !err) + }); +}, function(err, results){ // results now equals an array of the existing files }); @@ -395,25 +399,26 @@ __Related__ __Alias:__ `select` Returns a new array of all the values in `arr` which pass an async truth test. -_The callback for each `iteratee` call only accepts a single argument of `true` or -`false`; it does not accept an error argument first!_ This is in-line with the -way node libraries work with truth tests like `fs.exists`. This operation is -performed in parallel, but the results array will be in the same order as the -original. +This operation is performed in parallel, +but the results array will be in the same order as the original. __Arguments__ * `arr` - An array to iterate over. * `iteratee(item, callback)` - A truth test to apply to each item in `arr`. - The `iteratee` is passed a `callback(truthValue)`, which must be called with a + The `iteratee` is passed a `callback(err, truthValue)`, which must be called with a boolean argument once it has completed. -* `callback(results)` - *Optional* A callback which is called after all the `iteratee` +* `callback(err, results)` - *Optional* A callback which is called after all the `iteratee` functions have finished. __Example__ ```js -async.filter(['file1','file2','file3'], fs.exists, function(results){ +async.filter(['file1','file2','file3'], function(filePath, callback) { + fs.access(filePath, function(err) { + callback(null, !err) + }); +}, function(err, results){ // results now equals an array of the existing files }); ``` @@ -502,17 +507,21 @@ __Arguments__ * `arr` - An array to iterate over. * `iteratee(item, callback)` - A truth test to apply to each item in `arr`. - The iteratee is passed a `callback(truthValue)` which must be called with a - boolean argument once it has completed. **Note: this callback does not take an error as its first argument.** -* `callback(result)` - *Optional* A callback which is called as soon as any iteratee returns + The iteratee is passed a `callback(err, truthValue)` which must be called with a + boolean argument once it has completed. +* `callback(err, result)` - *Optional* A callback which is called as soon as any iteratee returns `true`, or after all the `iteratee` functions have finished. Result will be the first item in the array that passes the truth test (iteratee) or the - value `undefined` if none passed. **Note: this callback does not take an error as its first argument.** + value `undefined` if none passed. __Example__ ```js -async.detect(['file1','file2','file3'], fs.exists, function(result){ +async.detect(['file1','file2','file3'], function(filePath, callback) { + fs.access(filePath, function(err) { + callback(null, !err) + }); +}, function(err, result){ // result now equals the first file in the list that exists }); ``` @@ -581,10 +590,7 @@ async.sortBy([1,9,3,5], function(x, callback){ __Alias:__ `any` Returns `true` if at least one element in the `arr` satisfies an async test. -_The callback for each iteratee call only accepts a single argument of `true` or -`false`; it does not accept an error argument first!_ This is in-line with the -way node libraries work with truth tests like `fs.exists`. Once any iteratee -call returns `true`, the main `callback` is immediately called. +If any iteratee call returns `true`, the main `callback` is immediately called. __Arguments__ @@ -592,15 +598,18 @@ __Arguments__ * `iteratee(item, callback)` - A truth test to apply to each item in the array in parallel. The iteratee is passed a `callback(truthValue)`` which must be called with a boolean argument once it has completed. -* `callback(result)` - *Optional* A callback which is called as soon as any iteratee returns +* `callback(err, result)` - *Optional* A callback which is called as soon as any iteratee returns `true`, or after all the iteratee 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 -async.some(['file1','file2','file3'], fs.exists, function(result){ +async.some(['file1','file2','file3'], function(filePath, callback) { + fs.access(filePath, function(err) { + callback(null, !err) + }); +}, function(err, result){ // if result is true then at least one of the files exists }); ``` @@ -617,26 +626,26 @@ __Related__ __Alias:__ `all` Returns `true` if every element in `arr` satisfies an async test. -_The callback for each `iteratee` call only accepts a single argument of `true` or -`false`; it does not accept an error argument first!_ This is in-line with the -way node libraries work with truth tests like `fs.exists`. +If any iteratee call returns `false`, the main `callback` is immediately called. __Arguments__ * `arr` - An array to iterate over. * `iteratee(item, callback)` - A truth test to apply to each item in the array - in parallel. The iteratee is passed a `callback(truthValue)` which must be + in parallel. The iteratee is passed a `callback(err, truthValue)` which must be called with a boolean argument once it has completed. -* `callback(result)` - *Optional* A callback which is called as soon as any iteratee returns - `false`, or after all the iteratee 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.** +* `callback(err, result)` - *Optional* A callback which is called after all the `iteratee` + functions have finished. Result will be either `true` or `false` depending on + the values of the async tests. __Example__ ```js -async.every(['file1','file2','file3'], fs.exists, function(result){ +async.every(['file1','file2','file3'], function(filePath, callback) { + fs.access(filePath, function(err) { + callback(null, !err) + }); +}, function(err, result){ // if result is true then every file exists }); ``` diff --git a/lib/internal/createTester.js b/lib/internal/createTester.js index 1bf2e8904..0e9e75068 100644 --- a/lib/internal/createTester.js +++ b/lib/internal/createTester.js @@ -2,15 +2,26 @@ export default function _createTester(eachfn, check, getResult) { return function(arr, limit, iterator, cb) { - function done() { - if (cb) cb(getResult(false, void 0)); + function done(err) { + if (cb) { + if (err) { + cb(err); + } else { + cb(null, getResult(false, void 0)); + } + } } function iteratee(x, _, callback) { if (!cb) return callback(); - iterator(x, function (v) { - if (cb && check(v)) { - cb(getResult(true, x)); - cb = iterator = false; + iterator(x, function (err, v) { + if (cb) { + if (err) { + cb(err); + cb = iterator = false; + } else if (check(v)) { + cb(null, getResult(true, x)); + cb = iterator = false; + } } callback(); }); diff --git a/lib/internal/filter.js b/lib/internal/filter.js index 4d5ceb8e1..8b1de9fac 100644 --- a/lib/internal/filter.js +++ b/lib/internal/filter.js @@ -6,15 +6,25 @@ import property from 'lodash/_baseProperty'; export default function _filter(eachfn, arr, iterator, callback) { var results = []; eachfn(arr, function (x, index, callback) { - iterator(x, function (v) { - if (v) { - results.push({index: index, value: x}); + iterator(x, function (err, v) { + if (err) { + callback(err); + } + else { + if (v) { + results.push({index: index, value: x}); + } + callback(); } - callback(); }); - }, function () { - callback(arrayMap(results.sort(function (a, b) { - return a.index - b.index; - }), property('value'))); + }, function (err) { + if (err) { + callback(err); + } + else { + callback(null, arrayMap(results.sort(function (a, b) { + return a.index - b.index; + }), property('value'))); + } }); } diff --git a/lib/internal/reject.js b/lib/internal/reject.js index 8b1bf0d5c..708e810c5 100644 --- a/lib/internal/reject.js +++ b/lib/internal/reject.js @@ -4,8 +4,12 @@ import filter from './filter'; export default function reject(eachfn, arr, iterator, callback) { filter(eachfn, arr, function(value, cb) { - iterator(value, function(v) { - cb(!v); + iterator(value, function(err, v) { + if (err) { + cb(err); + } else { + cb(null, !v); + } }); }, callback); } diff --git a/test/test-async.js b/test/test-async.js index e27dd33d4..20bf43f37 100755 --- a/test/test-async.js +++ b/test/test-async.js @@ -39,14 +39,14 @@ function mapIterator(call_order, x, callback) { function filterIterator(x, callback) { setTimeout(function(){ - callback(x % 2); + callback(null, x % 2); }, x*25); } function detectIterator(call_order, x, callback) { setTimeout(function(){ call_order.push(x); - callback(x == 2); + callback(null, x == 2); }, x*25); } @@ -2161,7 +2161,8 @@ exports['transform error'] = function(test){ }; exports['filter'] = function(test){ - async.filter([3,1,2], filterIterator, function(results){ + async.filter([3,1,2], filterIterator, function(err, results){ + test.equals(err, null); test.same(results, [3,1]); test.done(); }); @@ -2170,16 +2171,28 @@ exports['filter'] = function(test){ exports['filter original untouched'] = function(test){ var a = [3,1,2]; async.filter(a, function(x, callback){ - callback(x % 2); - }, function(results){ + callback(null, x % 2); + }, function(err, results){ + test.equals(err, null); test.same(results, [3,1]); test.same(a, [3,1,2]); test.done(); }); }; +exports['filter error'] = function(test){ + async.filter([3,1,2], function(x, callback){ + callback('error'); + } , function(err, results){ + test.equals(err, 'error'); + test.equals(results, null); + test.done(); + }); +}; + exports['filterSeries'] = function(test){ - async.filterSeries([3,1,2], filterIterator, function(results){ + async.filterSeries([3,1,2], filterIterator, function(err, results){ + test.equals(err, null); test.same(results, [3,1]); test.done(); }); @@ -2196,28 +2209,42 @@ exports['selectSeries alias'] = function(test){ }; exports['reject'] = function(test){ - test.expect(1); - async.reject([3,1,2], filterIterator, function(results){ + test.expect(2); + async.reject([3,1,2], filterIterator, function(err, results){ + test.equals(err, null); test.same(results, [2]); test.done(); }); }; exports['reject original untouched'] = function(test){ - test.expect(2); + test.expect(3); var a = [3,1,2]; async.reject(a, function(x, callback){ - callback(x % 2); - }, function(results){ + callback(null, x % 2); + }, function(err, results){ + test.equals(err, null); test.same(results, [2]); test.same(a, [3,1,2]); test.done(); }); }; +exports['reject error'] = function(test){ + test.expect(2); + async.reject([3,1,2], function(x, callback){ + callback('error'); + } , function(err, results){ + test.equals(err, 'error'); + test.equals(results, null); + test.done(); + }); +}; + exports['rejectSeries'] = function(test){ - test.expect(1); - async.rejectSeries([3,1,2], filterIterator, function(results){ + test.expect(2); + async.rejectSeries([3,1,2], filterIterator, function(err, results){ + test.equals(err, null); test.same(results, [2]); test.done(); }); @@ -2237,40 +2264,44 @@ function testLimit(test, arr, limitFunc, limit, iter, done) { } exports['rejectLimit'] = function(test) { - test.expect(2); + test.expect(3); testLimit(test, [5, 4, 3, 2, 1], async.rejectLimit, 2, function(v, next) { - next(v % 2); - }, function(x) { - test.same(x, [4, 2]); + next(null, v % 2); + }, function(err, result){ + test.equals(err, null); + test.same(result, [4, 2]); test.done(); }); }; exports['filterLimit'] = function(test) { - test.expect(2); + test.expect(3); testLimit(test, [5, 4, 3, 2, 1], async.filterLimit, 2, function(v, next) { - next(v % 2); - }, function(x) { - test.same(x, [5, 3, 1]); + next(null, v % 2); + }, function(err, result){ + test.equals(err, null); + test.same(result, [5, 3, 1]); test.done(); }); }; exports['some true'] = function(test){ - test.expect(1); + test.expect(2); async.some([3,1,2], function(x, callback){ - setTimeout(function(){callback(x === 1);}, 0); - }, function(result){ + setTimeout(function(){callback(null, x === 1);}, 0); + }, function(err, result){ + test.equals(err, null); test.equals(result, true); test.done(); }); }; exports['some false'] = function(test){ - test.expect(1); + test.expect(2); async.some([3,1,2], function(x, callback){ - setTimeout(function(){callback(x === 10);}, 0); - }, function(result){ + setTimeout(function(){callback(null, x === 10);}, 0); + }, function(err, result){ + test.equals(err, null); test.equals(result, false); test.done(); }); @@ -2282,7 +2313,7 @@ exports['some early return'] = function(test){ async.some([1,2,3], function(x, callback){ setTimeout(function(){ call_order.push(x); - callback(x === 1); + callback(null, x === 1); }, x*25); }, function(){ call_order.push('callback'); @@ -2293,10 +2324,22 @@ exports['some early return'] = function(test){ }, 100); }; +exports['some error'] = function(test){ + test.expect(2); + async.some([3,1,2], function(x, callback){ + setTimeout(function(){callback('error');}, 0); + }, function(err, result){ + test.equals(err, 'error'); + test.equals(result, null); + test.done(); + }); +}; + exports['someLimit true'] = function(test){ async.someLimit([3,1,2], 2, function(x, callback){ - setTimeout(function(){callback(x === 2);}, 0); - }, function(result){ + setTimeout(function(){callback(null, x === 2);}, 0); + }, function(err, result){ + test.equals(err, null); test.equals(result, true); test.done(); }); @@ -2304,8 +2347,9 @@ exports['someLimit true'] = function(test){ exports['someLimit false'] = function(test){ async.someLimit([3,1,2], 2, function(x, callback){ - setTimeout(function(){callback(x === 10);}, 0); - }, function(result){ + setTimeout(function(){callback(null, x === 10);}, 0); + }, function(err, result){ + test.equals(err, null); test.equals(result, false); test.done(); }); @@ -2313,8 +2357,9 @@ exports['someLimit false'] = function(test){ exports['every true'] = function(test){ async.everyLimit([3,1,2], 1, function(x, callback){ - setTimeout(function(){callback(x > 1);}, 0); - }, function(result){ + setTimeout(function(){callback(null, x > 1);}, 0); + }, function(err, result){ + test.equals(err, null); test.equals(result, true); test.done(); }); @@ -2322,20 +2367,22 @@ exports['every true'] = function(test){ exports['everyLimit false'] = function(test){ async.everyLimit([3,1,2], 2, function(x, callback){ - setTimeout(function(){callback(x === 2);}, 0); - }, function(result){ + setTimeout(function(){callback(null, x === 2);}, 0); + }, function(err, result){ + test.equals(err, null); test.equals(result, false); test.done(); }); }; exports['everyLimit short-circuit'] = function(test){ - test.expect(2); + test.expect(3); var calls = 0; async.everyLimit([3,1,2], 1, function(x, callback){ calls++; - callback(x === 1); - }, function(result){ + callback(null, x === 1); + }, function(err, result){ + test.equals(err, null); test.equals(result, false); test.equals(calls, 1); test.done(); @@ -2344,12 +2391,13 @@ exports['everyLimit short-circuit'] = function(test){ exports['someLimit short-circuit'] = function(test){ - test.expect(2); + test.expect(3); var calls = 0; async.someLimit([3,1,2], 1, function(x, callback){ calls++; - callback(x === 1); - }, function(result){ + callback(null, x === 1); + }, function(err, result){ + test.equals(err, null); test.equals(result, true); test.equals(calls, 2); test.done(); @@ -2362,20 +2410,22 @@ exports['any alias'] = function(test){ }; exports['every true'] = function(test){ - test.expect(1); + test.expect(2); async.every([1,2,3], function(x, callback){ - setTimeout(function(){callback(true);}, 0); - }, function(result){ + setTimeout(function(){callback(null, true);}, 0); + }, function(err, result){ + test.equals(err, null); test.equals(result, true); test.done(); }); }; exports['every false'] = function(test){ - test.expect(1); + test.expect(2); async.every([1,2,3], function(x, callback){ - setTimeout(function(){callback(x % 2);}, 0); - }, function(result){ + setTimeout(function(){callback(null, x % 2);}, 0); + }, function(err, result){ + test.equals(err, null); test.equals(result, false); test.done(); }); @@ -2387,7 +2437,7 @@ exports['every early return'] = function(test){ async.every([1,2,3], function(x, callback){ setTimeout(function(){ call_order.push(x); - callback(x === 1); + callback(null, x === 1); }, x*25); }, function(){ call_order.push('callback'); @@ -2398,16 +2448,27 @@ exports['every early return'] = function(test){ }, 100); }; +exports['every error'] = function(test){ + async.every([1,2,3], function(x, callback){ + setTimeout(function(){callback('error');}, 0); + }, function(err, result){ + test.equals(err, 'error'); + test.equals(result, null); + test.done(); + }); +}; + exports['all alias'] = function(test){ test.equals(async.all, async.every); test.done(); }; exports['detect'] = function(test){ - test.expect(2); + test.expect(3); var call_order = []; - async.detect([3,2,1], detectIterator.bind(this, call_order), function(result){ + async.detect([3,2,1], detectIterator.bind(this, call_order), function(err, result){ call_order.push('callback'); + test.equals(err, null); test.equals(result, 2); }); setTimeout(function(){ @@ -2417,10 +2478,11 @@ exports['detect'] = function(test){ }; exports['detect - mulitple matches'] = function(test){ - test.expect(2); + test.expect(3); var call_order = []; - async.detect([3,2,2,1,2], detectIterator.bind(this, call_order), function(result){ + async.detect([3,2,2,1,2], detectIterator.bind(this, call_order), function(err, result){ call_order.push('callback'); + test.equals(err, null); test.equals(result, 2); }); setTimeout(function(){ @@ -2429,11 +2491,23 @@ exports['detect - mulitple matches'] = function(test){ }, 100); }; -exports['detectSeries'] = function(test){ +exports['detect error'] = function(test){ test.expect(2); + async.detect([3,2,1], function(x, callback) { + setTimeout(function(){callback('error');}, 0); + }, function(err, result){ + test.equals(err, 'error'); + test.equals(result, null); + test.done(); + }); +}; + +exports['detectSeries'] = function(test){ + test.expect(3); var call_order = []; - async.detectSeries([3,2,1], detectIterator.bind(this, call_order), function(result){ + async.detectSeries([3,2,1], detectIterator.bind(this, call_order), function(err, result){ call_order.push('callback'); + test.equals(err, null); test.equals(result, 2); }); setTimeout(function(){ @@ -2443,10 +2517,11 @@ exports['detectSeries'] = function(test){ }; exports['detectSeries - multiple matches'] = function(test){ - test.expect(2); + test.expect(3); var call_order = []; - async.detectSeries([3,2,2,1,2], detectIterator.bind(this, call_order), function(result){ + async.detectSeries([3,2,2,1,2], detectIterator.bind(this, call_order), function(err, result){ call_order.push('callback'); + test.equals(err, null); test.equals(result, 2); }); setTimeout(function(){ @@ -2456,21 +2531,23 @@ exports['detectSeries - multiple matches'] = function(test){ }; exports['detectSeries - ensure stop'] = function (test) { - test.expect(1); + test.expect(2); 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) { + cb(null, num === 3); + }, function (err, result) { + test.equals(err, null); test.equals(result, 3); test.done(); }); }; exports['detectLimit'] = function(test){ - test.expect(2); + test.expect(3); var call_order = []; - async.detectLimit([3, 2, 1], 2, detectIterator.bind(this, call_order), function(result) { + async.detectLimit([3, 2, 1], 2, detectIterator.bind(this, call_order), function(err, result) { call_order.push('callback'); + test.equals(err, null); test.equals(result, 2); }); setTimeout(function() { @@ -2480,10 +2557,11 @@ exports['detectLimit'] = function(test){ }; exports['detectLimit - multiple matches'] = function(test){ - test.expect(2); + test.expect(3); var call_order = []; - async.detectLimit([3,2,2,1,2], 2, detectIterator.bind(this, call_order), function(result){ + async.detectLimit([3,2,2,1,2], 2, detectIterator.bind(this, call_order), function(err, result){ call_order.push('callback'); + test.equals(err, null); test.equals(result, 2); }); setTimeout(function(){ @@ -2493,11 +2571,12 @@ exports['detectLimit - multiple matches'] = function(test){ }; exports['detectLimit - ensure stop'] = function (test) { - test.expect(1); + test.expect(2); async.detectLimit([1, 2, 3, 4, 5], 2, function (num, cb) { if (num > 4) throw new Error("detectLimit did not stop iterating"); - cb(num === 3); - }, function (result) { + cb(null, num === 3); + }, function (err, result) { + test.equals(err, null); test.equals(result, 3); test.done(); });