summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--.npmignore5
-rw-r--r--README.md140
-rw-r--r--dist/async.min.js1
-rw-r--r--index.js3
-rw-r--r--lib/async.js227
-rw-r--r--package.json42
-rw-r--r--test/test-async.js414
-rw-r--r--test/test-strict.js19
9 files changed, 773 insertions, 81 deletions
diff --git a/.gitignore b/.gitignore
index 3c3629e..8225baa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
-node_modules
+/node_modules
+/dist
diff --git a/.npmignore b/.npmignore
index 9bdfc97..392d66e 100644
--- a/.npmignore
+++ b/.npmignore
@@ -1,4 +1,7 @@
deps
dist
test
-nodelint.cfg \ No newline at end of file
+nodelint.cfg
+.npmignore
+.gitmodules
+Makefile
diff --git a/README.md b/README.md
index ce3a354..40b1c0f 100644
--- a/README.md
+++ b/README.md
@@ -14,7 +14,7 @@ callback as the last argument of your async function.
## Quick Examples
-```js
+```javascript
async.map(['file1','file2','file3'], fs.stat, function(err, results){
// results is now an array of stats for each file
});
@@ -88,9 +88,12 @@ So far its been tested in IE6, IE7, IE8, FF3.6 and Chrome 5. Usage:
* [series](#series)
* [parallel](#parallel)
* [whilst](#whilst)
+* [doWhilst](#doWhilst)
* [until](#until)
+* [doUntil](#doUntil)
* [waterfall](#waterfall)
* [queue](#queue)
+* [cargo](#cargo)
* [auto](#auto)
* [iterator](#iterator)
* [apply](#apply)
@@ -226,6 +229,35 @@ processing. The results array will be in the same order as the original.
---------------------------------------
+<a name="mapLimit" />
+### mapLimit(arr, limit, iterator, callback)
+
+The same as map only the iterator is applied to batches of items in the
+array, in series. The next batch of iterators is only called once the current
+one has completed processing.
+
+__Arguments__
+
+* arr - An array to iterate over.
+* limit - How many items should be in each batch.
+* iterator(item, callback) - A function to apply to each item in the array.
+ The iterator is passed a callback which must be called once it has completed.
+ If no error has occured, the callback should be run without arguments or
+ with an explicit null argument.
+* callback(err, results) - A callback which is called after all the iterator
+ functions have finished, or an error has occurred. Results is an array of the
+ transformed items from the original array.
+
+__Example__
+
+```js
+async.map(['file1','file2','file3'], 1, fs.stat, function(err, results){
+ // results is now an array of stats for each file
+});
+```
+
+---------------------------------------
+
<a name="filter" />
### filter(arr, iterator, callback)
@@ -537,7 +569,7 @@ async.series([
function(callback){
// do some more stuff ...
callback(null, 'two');
- },
+ }
],
// optional callback
function(err, results){
@@ -556,7 +588,7 @@ async.series({
setTimeout(function(){
callback(null, 2);
}, 100);
- },
+ }
},
function(err, results) {
// results is now equal to: {one: 1, two: 2}
@@ -601,7 +633,7 @@ async.parallel([
setTimeout(function(){
callback(null, 'two');
}, 100);
- },
+ }
],
// optional callback
function(err, results){
@@ -621,7 +653,7 @@ async.parallel({
setTimeout(function(){
callback(null, 2);
}, 100);
- },
+ }
},
function(err, results) {
// results is now equals to: {one: 1, two: 2}
@@ -630,6 +662,24 @@ function(err, results) {
---------------------------------------
+<a name="parallel" />
+### parallelLimit(tasks, limit, [callback])
+
+The same as parallel only the tasks are executed in parallel with a maximum of "limit"
+tasks executing at any time.
+
+__Arguments__
+
+* tasks - An array or object containing functions to run, each function is passed a
+ callback it must call on completion.
+* limit - The maximum number of tasks to run at any time.
+* callback(err, results) - An optional callback to run once all the functions
+ have completed. This function gets an array of all the arguments passed to
+ the callbacks used in the array.
+
+
+---------------------------------------
+
<a name="whilst" />
### whilst(test, fn, callback)
@@ -664,6 +714,13 @@ async.whilst(
---------------------------------------
+<a name="doWhilst" />
+### doWhilst(fn, test, callback)
+
+The post check version of whilst. To reflect the difference in the order of operations `test` and `fn` arguments are switched. `doWhilst` is to `whilst` as `do while` is to `while` in plain JavaScript.
+
+---------------------------------------
+
<a name="until" />
### until(test, fn, callback)
@@ -672,6 +729,13 @@ or an error occurs.
The inverse of async.whilst.
+---------------------------------------
+
+<a name="doUntil" />
+### doUntil(fn, test, callback)
+
+Like doWhilst except the test is inverted. Note the argument ordering differs from `until`.
+
---------------------------------------
@@ -778,6 +842,63 @@ q.push([{name: 'baz'},{name: 'bay'},{name: 'bax'}], function (err) {
---------------------------------------
+<a name="cargo" />
+### cargo(worker, [payload])
+
+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 is available. Once
+the worker has completed some tasks, each callback of those tasks is called.
+
+__Arguments__
+
+* worker(tasks, callback) - An asynchronous function for processing queued
+ tasks.
+* payload - An optional integer for determining how many tasks should be
+ process per round, default is unlimited.
+
+__Cargo objects__
+
+The cargo object returned by this function has the following properties and
+methods:
+
+* length() - a function returning the number of items waiting to be processed.
+* payload - an integer for determining how many tasks should be
+ process per round. This property can be changed after a cargo is created to
+ alter the payload on-the-fly.
+* push(task, [callback]) - add a new task to the queue, the callback is called
+ once the worker has finished processing the task.
+ instead of a single task, an array of tasks can be submitted. the respective callback is used for every task in the list.
+* saturated - a callback that is called when the queue length hits the concurrency and further tasks will be queued
+* empty - a callback that is called when the last item from the queue is given to a worker
+* drain - a callback that is called when the last item from the queue has returned from the worker
+
+__Example__
+
+```js
+// create a cargo object with payload 2
+
+var cargo = async.cargo(function (task, callback) {
+ console.log('hello ' + task.name);
+ callback();
+}, 2);
+
+
+// add some items
+
+cargo.push({name: 'foo'}, function (err) {
+ console.log('finished processing foo');
+});
+cargo.push({name: 'bar'}, function (err) {
+ console.log('finished processing bar');
+});
+cargo.push({name: 'baz'}, function (err) {
+ console.log('finished processing baz');
+});
+```
+
+---------------------------------------
+
<a name="auto" />
### auto(tasks, [callback])
@@ -836,13 +957,13 @@ async.parallel([
// this is run at the same time as getting the data
}
],
-function(results){
+function(err, results){
async.series([
function(callback){
// once there is some data and the directory exists,
// write the data to a file in the directory
},
- email_link: function(callback){
+ function(callback){
// once the file is written let's email a link to it...
}
]);
@@ -925,7 +1046,7 @@ async.parallel([
},
function(callback){
fs.writeFile('testfile2', 'test2', callback);
- },
+ }
]);
```
@@ -975,6 +1096,9 @@ 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.
+The cache of results is exposed as the `memo` property of the function returned
+by `memoize`.
+
__Arguments__
* fn - the function you to proxy and cache results from.
diff --git a/dist/async.min.js b/dist/async.min.js
deleted file mode 100644
index 6ba3145..0000000
--- a/dist/async.min.js
+++ /dev/null
@@ -1 +0,0 @@
-/*global setTimeout: false, console: false */(function(){var a={},b=this,c=b.async;typeof module!="undefined"&&module.exports?module.exports=a:b.async=a,a.noConflict=function(){return b.async=c,a};var d=function(a,b){if(a.forEach)return a.forEach(b);for(var c=0;c<a.length;c+=1)b(a[c],c,a)},e=function(a,b){if(a.map)return a.map(b);var c=[];return d(a,function(a,d,e){c.push(b(a,d,e))}),c},f=function(a,b,c){return a.reduce?a.reduce(b,c):(d(a,function(a,d,e){c=b(c,a,d,e)}),c)},g=function(a){if(Object.keys)return Object.keys(a);var b=[];for(var c in a)a.hasOwnProperty(c)&&b.push(c);return b};typeof process=="undefined"||!process.nextTick?a.nextTick=function(a){setTimeout(a,0)}:a.nextTick=process.nextTick,a.forEach=function(a,b,c){c=c||function(){};if(!a.length)return c();var e=0;d(a,function(d){b(d,function(b){b?(c(b),c=function(){}):(e+=1,e===a.length&&c(null))})})},a.forEachSeries=function(a,b,c){c=c||function(){};if(!a.length)return c();var d=0,e=function(){b(a[d],function(b){b?(c(b),c=function(){}):(d+=1,d===a.length?c(null):e())})};e()},a.forEachLimit=function(a,b,c,d){d=d||function(){};if(!a.length||b<=0)return d();var e=0,f=0,g=0;(function h(){if(e===a.length)return d();while(g<b&&f<a.length)f+=1,g+=1,c(a[f-1],function(b){b?(d(b),d=function(){}):(e+=1,g-=1,e===a.length?d():h())})})()};var h=function(b){return function(){var c=Array.prototype.slice.call(arguments);return b.apply(null,[a.forEach].concat(c))}},i=function(b){return function(){var c=Array.prototype.slice.call(arguments);return b.apply(null,[a.forEachSeries].concat(c))}},j=function(a,b,c,d){var f=[];b=e(b,function(a,b){return{index:b,value:a}}),a(b,function(a,b){c(a.value,function(c,d){f[a.index]=d,b(c)})},function(a){d(a,f)})};a.map=h(j),a.mapSeries=i(j),a.reduce=function(b,c,d,e){a.forEachSeries(b,function(a,b){d(c,a,function(a,d){c=d,b(a)})},function(a){e(a,c)})},a.inject=a.reduce,a.foldl=a.reduce,a.reduceRight=function(b,c,d,f){var g=e(b,function(a){return a}).reverse();a.reduce(g,c,d,f)},a.foldr=a.reduceRight;var k=function(a,b,c,d){var f=[];b=e(b,function(a,b){return{index:b,value:a}}),a(b,function(a,b){c(a.value,function(c){c&&f.push(a),b()})},function(a){d(e(f.sort(function(a,b){return a.index-b.index}),function(a){return a.value}))})};a.filter=h(k),a.filterSeries=i(k),a.select=a.filter,a.selectSeries=a.filterSeries;var l=function(a,b,c,d){var f=[];b=e(b,function(a,b){return{index:b,value:a}}),a(b,function(a,b){c(a.value,function(c){c||f.push(a),b()})},function(a){d(e(f.sort(function(a,b){return a.index-b.index}),function(a){return a.value}))})};a.reject=h(l),a.rejectSeries=i(l);var m=function(a,b,c,d){a(b,function(a,b){c(a,function(c){c?(d(a),d=function(){}):b()})},function(a){d()})};a.detect=h(m),a.detectSeries=i(m),a.some=function(b,c,d){a.forEach(b,function(a,b){c(a,function(a){a&&(d(!0),d=function(){}),b()})},function(a){d(!1)})},a.any=a.some,a.every=function(b,c,d){a.forEach(b,function(a,b){c(a,function(a){a||(d(!1),d=function(){}),b()})},function(a){d(!0)})},a.all=a.every,a.sortBy=function(b,c,d){a.map(b,function(a,b){c(a,function(c,d){c?b(c):b(null,{value:a,criteria:d})})},function(a,b){if(a)return d(a);var c=function(a,b){var c=a.criteria,d=b.criteria;return c<d?-1:c>d?1:0};d(null,e(b.sort(c),function(a){return a.value}))})},a.auto=function(a,b){b=b||function(){};var c=g(a);if(!c.length)return b(null);var e={},h=[],i=function(a){h.unshift(a)},j=function(a){for(var b=0;b<h.length;b+=1)if(h[b]===a){h.splice(b,1);return}},k=function(){d(h.slice(0),function(a){a()})};i(function(){g(e).length===c.length&&(b(null,e),b=function(){})}),d(c,function(c){var d=a[c]instanceof Function?[a[c]]:a[c],g=function(a){if(a)b(a),b=function(){};else{var d=Array.prototype.slice.call(arguments,1);d.length<=1&&(d=d[0]),e[c]=d,k()}},h=d.slice(0,Math.abs(d.length-1))||[],l=function(){return f(h,function(a,b){return a&&e.hasOwnProperty(b)},!0)&&!e.hasOwnProperty(c)};if(l())d[d.length-1](g,e);else{var m=function(){l()&&(j(m),d[d.length-1](g,e))};i(m)}})},a.waterfall=function(b,c){c=c||function(){};if(!b.length)return c();var d=function(b){return function(e){if(e)c(e),c=function(){};else{var f=Array.prototype.slice.call(arguments,1),g=b.next();g?f.push(d(g)):f.push(c),a.nextTick(function(){b.apply(null,f)})}}};d(a.iterator(b))()},a.parallel=function(b,c){c=c||function(){};if(b.constructor===Array)a.map(b,function(a,b){a&&a(function(a){var c=Array.prototype.slice.call(arguments,1);c.length<=1&&(c=c[0]),b.call(null,a,c)})},c);else{var d={};a.forEach(g(b),function(a,c){b[a](function(b){var e=Array.prototype.slice.call(arguments,1);e.length<=1&&(e=e[0]),d[a]=e,c(b)})},function(a){c(a,d)})}},a.series=function(b,c){c=c||function(){};if(b.constructor===Array)a.mapSeries(b,function(a,b){a&&a(function(a){var c=Array.prototype.slice.call(arguments,1);c.length<=1&&(c=c[0]),b.call(null,a,c)})},c);else{var d={};a.forEachSeries(g(b),function(a,c){b[a](function(b){var e=Array.prototype.slice.call(arguments,1);e.length<=1&&(e=e[0]),d[a]=e,c(b)})},function(a){c(a,d)})}},a.iterator=function(a){var b=function(c){var d=function(){return a.length&&a[c].apply(null,arguments),d.next()};return d.next=function(){return c<a.length-1?b(c+1):null},d};return b(0)},a.apply=function(a){var b=Array.prototype.slice.call(arguments,1);return function(){return a.apply(null,b.concat(Array.prototype.slice.call(arguments)))}};var n=function(a,b,c,d){var e=[];a(b,function(a,b){c(a,function(a,c){e=e.concat(c||[]),b(a)})},function(a){d(a,e)})};a.concat=h(n),a.concatSeries=i(n),a.whilst=function(b,c,d){b()?c(function(e){if(e)return d(e);a.whilst(b,c,d)}):d()},a.until=function(b,c,d){b()?d():c(function(e){if(e)return d(e);a.until(b,c,d)})},a.queue=function(b,c){var e=0,f={tasks:[],concurrency:c,saturated:null,empty:null,drain:null,push:function(b,e){b.constructor!==Array&&(b=[b]),d(b,function(b){f.tasks.push({data:b,callback:typeof e=="function"?e:null}),f.saturated&&f.tasks.length==c&&f.saturated(),a.nextTick(f.process)})},process:function(){if(e<f.concurrency&&f.tasks.length){var a=f.tasks.shift();f.empty&&f.tasks.length==0&&f.empty(),e+=1,b(a.data,function(){e-=1,a.callback&&a.callback.apply(a,arguments),f.drain&&f.tasks.length+e==0&&f.drain(),f.process()})}},length:function(){return f.tasks.length},running:function(){return e}};return f};var o=function(a){return function(b){var c=Array.prototype.slice.call(arguments,1);b.apply(null,c.concat([function(b){var c=Array.prototype.slice.call(arguments,1);typeof console!="undefined"&&(b?console.error&&console.error(b):console[a]&&d(c,function(b){console[a](b)}))}]))}};a.log=o("log"),a.dir=o("dir"),a.memoize=function(a,b){var c={},d={};b=b||function(a){return a};var e=function(){var e=Array.prototype.slice.call(arguments),f=e.pop(),g=b.apply(null,e);g in c?f.apply(null,c[g]):g in d?d[g].push(f):(d[g]=[f],a.apply(null,e.concat([function(){c[g]=arguments;var a=d[g];delete d[g];for(var b=0,e=a.length;b<e;b++)a[b].apply(null,arguments)}])))};return e.unmemoized=a,e},a.unmemoize=function(a){return function(){return(a.unmemoized||a).apply(null,arguments)}}})(); \ No newline at end of file
diff --git a/index.js b/index.js
deleted file mode 100644
index 8e23845..0000000
--- a/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-// This file is just added for convenience so this repository can be
-// directly checked out into a project's deps folder
-module.exports = require('./lib/async');
diff --git a/lib/async.js b/lib/async.js
index 7cc4f5e..baf5820 100644
--- a/lib/async.js
+++ b/lib/async.js
@@ -4,14 +4,11 @@
var async = {};
// global on the server, window in the browser
- var root = this,
- previous_async = root.async;
+ var root, previous_async;
- if (typeof module !== 'undefined' && module.exports) {
- module.exports = async;
- }
- else {
- root.async = async;
+ root = this;
+ if (root != null) {
+ previous_async = root.async;
}
async.noConflict = function () {
@@ -19,6 +16,15 @@
return async;
};
+ function only_once(fn) {
+ var called = false;
+ return function() {
+ if (called) throw new Error("Callback was already called.");
+ called = true;
+ fn.apply(root, arguments);
+ }
+ }
+
//// cross-browser compatiblity functions ////
var _forEach = function (arr, iterator) {
@@ -83,7 +89,7 @@
}
var completed = 0;
_forEach(arr, function (x) {
- iterator(x, function (err) {
+ iterator(x, only_once(function (err) {
if (err) {
callback(err);
callback = function () {};
@@ -94,7 +100,7 @@
callback(null);
}
}
- });
+ }));
});
};
@@ -125,40 +131,48 @@
};
async.forEachLimit = function (arr, limit, iterator, callback) {
- callback = callback || function () {};
- if (!arr.length || limit <= 0) {
- return callback();
- }
- var completed = 0;
- var started = 0;
- var running = 0;
+ var fn = _forEachLimit(limit);
+ fn.apply(null, [arr, iterator, callback]);
+ };
+
+ var _forEachLimit = function (limit) {
- (function replenish () {
- if (completed === arr.length) {
+ return function (arr, iterator, callback) {
+ callback = callback || function () {};
+ if (!arr.length || limit <= 0) {
return callback();
}
+ var completed = 0;
+ var started = 0;
+ var running = 0;
- 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();
+ (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 {
- replenish();
+ completed += 1;
+ running -= 1;
+ if (completed === arr.length) {
+ callback();
+ }
+ else {
+ replenish();
+ }
}
- }
- });
- }
- })();
+ });
+ }
+ })();
+ };
};
@@ -168,6 +182,12 @@
return fn.apply(null, [async.forEach].concat(args));
};
};
+ var doParallelLimit = function(limit, fn) {
+ return function () {
+ var args = Array.prototype.slice.call(arguments);
+ return fn.apply(null, [_forEachLimit(limit)].concat(args));
+ };
+ };
var doSeries = function (fn) {
return function () {
var args = Array.prototype.slice.call(arguments);
@@ -192,7 +212,13 @@
};
async.map = doParallel(_asyncMap);
async.mapSeries = doSeries(_asyncMap);
+ async.mapLimit = function (arr, limit, iterator, callback) {
+ return _mapLimit(limit)(arr, iterator, callback);
+ };
+ var _mapLimit = function(limit) {
+ return doParallelLimit(limit, _asyncMap);
+ };
// reduce only has a series version, as doing reduce in parallel won't
// work in many situations.
@@ -425,7 +451,7 @@
var wrapIterator = function (iterator) {
return function (err) {
if (err) {
- callback(err);
+ callback.apply(null, arguments);
callback = function () {};
}
else {
@@ -446,10 +472,10 @@
wrapIterator(async.iterator(tasks))();
};
- async.parallel = function (tasks, callback) {
+ var _parallel = function(eachfn, tasks, callback) {
callback = callback || function () {};
if (tasks.constructor === Array) {
- async.map(tasks, function (fn, callback) {
+ eachfn.map(tasks, function (fn, callback) {
if (fn) {
fn(function (err) {
var args = Array.prototype.slice.call(arguments, 1);
@@ -463,7 +489,7 @@
}
else {
var results = {};
- async.forEach(_keys(tasks), function (k, callback) {
+ eachfn.forEach(_keys(tasks), function (k, callback) {
tasks[k](function (err) {
var args = Array.prototype.slice.call(arguments, 1);
if (args.length <= 1) {
@@ -478,6 +504,14 @@
}
};
+ async.parallel = function (tasks, callback) {
+ _parallel({ map: async.map, forEach: async.forEach }, tasks, callback);
+ };
+
+ async.parallelLimit = function(tasks, limit, callback) {
+ _parallel({ map: _mapLimit(limit), forEach: _forEachLimit(limit) }, tasks, callback);
+ };
+
async.series = function (tasks, callback) {
callback = callback || function () {};
if (tasks.constructor === Array) {
@@ -563,6 +597,20 @@
}
};
+ async.doWhilst = function (iterator, test, callback) {
+ iterator(function (err) {
+ if (err) {
+ return callback(err);
+ }
+ if (test()) {
+ async.doWhilst(iterator, test, callback);
+ }
+ else {
+ callback();
+ }
+ });
+ };
+
async.until = function (test, iterator, callback) {
if (!test()) {
iterator(function (err) {
@@ -577,6 +625,20 @@
}
};
+ async.doUntil = function (iterator, test, callback) {
+ iterator(function (err) {
+ if (err) {
+ return callback(err);
+ }
+ if (!test()) {
+ async.doUntil(iterator, test, callback);
+ }
+ else {
+ callback();
+ }
+ });
+ };
+
async.queue = function (worker, concurrency) {
var workers = 0;
var q = {
@@ -605,14 +667,14 @@
var task = q.tasks.shift();
if(q.empty && q.tasks.length == 0) q.empty();
workers += 1;
- worker(task.data, function () {
+ worker(task.data, only_once(function() {
workers -= 1;
if (task.callback) {
task.callback.apply(task, arguments);
}
if(q.drain && q.tasks.length + workers == 0) q.drain();
q.process();
- });
+ }));
}
},
length: function () {
@@ -625,6 +687,71 @@
return q;
};
+ async.cargo = function (worker, payload) {
+ var working = false,
+ tasks = [];
+
+ var cargo = {
+ tasks: tasks,
+ payload: payload,
+ saturated: null,
+ empty: null,
+ drain: null,
+ push: function (data, callback) {
+ if(data.constructor !== Array) {
+ data = [data];
+ }
+ _forEach(data, function(task) {
+ tasks.push({
+ data: task,
+ callback: typeof callback === 'function' ? callback : null
+ });
+ if (cargo.saturated && tasks.length === payload) {
+ cargo.saturated();
+ }
+ });
+ async.nextTick(cargo.process);
+ },
+ process: function process() {
+ if (working) return;
+ if (tasks.length === 0) {
+ if(cargo.drain) cargo.drain();
+ return;
+ }
+
+ var ts = typeof payload === 'number'
+ ? tasks.splice(0, payload)
+ : tasks.splice(0);
+
+ var ds = _map(ts, function (task) {
+ return task.data;
+ });
+
+ if(cargo.empty) cargo.empty();
+ working = true;
+ worker(ds, function () {
+ working = false;
+
+ var args = arguments;
+ _forEach(ts, function (data) {
+ if (data.callback) {
+ data.callback.apply(null, args);
+ }
+ });
+
+ process();
+ });
+ },
+ length: function () {
+ return tasks.length;
+ },
+ running: function () {
+ return working;
+ }
+ };
+ return cargo;
+ };
+
var _console_fn = function (name) {
return function (fn) {
var args = Array.prototype.slice.call(arguments, 1);
@@ -679,6 +806,7 @@
}]));
}
};
+ memoized.memo = memo;
memoized.unmemoized = fn;
return memoized;
};
@@ -689,4 +817,19 @@
};
};
+ // AMD / RequireJS
+ if (typeof define !== 'undefined' && define.amd) {
+ define('async', [], function () {
+ return async;
+ });
+ }
+ // Node.js
+ else if (typeof module !== 'undefined' && module.exports) {
+ module.exports = async;
+ }
+ // included directly via <script> tag
+ else {
+ root.async = async;
+ }
+
}());
diff --git a/package.json b/package.json
index 39bc4ff..253e46e 100644
--- a/package.json
+++ b/package.json
@@ -1,21 +1,25 @@
-{ "name": "async"
-, "description": "Higher-order functions and common patterns for asynchronous code"
-, "main": "./index"
-, "author": "Caolan McMahon"
-, "version": "0.1.22"
-, "repository" :
- { "type" : "git"
- , "url" : "http://github.com/caolan/async.git"
- }
-, "bugs" : { "url" : "http://github.com/caolan/async/issues" }
-, "licenses" :
- [ { "type" : "MIT"
- , "url" : "http://github.com/caolan/async/raw/master/LICENSE"
+{
+ "name": "async",
+ "description": "Higher-order functions and common patterns for asynchronous code",
+ "main": "./lib/async",
+ "author": "Caolan McMahon",
+ "version": "0.1.23",
+ "repository" : {
+ "type" : "git",
+ "url" : "http://github.com/caolan/async.git"
+ },
+ "bugs" : {
+ "url" : "http://github.com/caolan/async/issues"
+ },
+ "licenses" : [
+ {
+ "type" : "MIT",
+ "url" : "http://github.com/caolan/async/raw/master/LICENSE"
+ }
+ ],
+ "devDependencies": {
+ "nodeunit": ">0.0.0",
+ "uglify-js": "1.2.x",
+ "nodelint": ">0.0.0"
}
- ]
-, "devDependencies":
- { "nodeunit": ">0.0.0"
- , "uglify-js": "1.2.x"
- , "nodelint": ">0.0.0"
- }
}
diff --git a/test/test-async.js b/test/test-async.js
index 52f5536..69390fa 100644
--- a/test/test-async.js
+++ b/test/test-async.js
@@ -49,19 +49,19 @@ function getFunctionsObject(call_order) {
setTimeout(function(){
call_order.push(1);
callback(null, 1);
- }, 25);
+ }, 100);
},
two: function(callback){
setTimeout(function(){
call_order.push(2);
callback(null, 2);
- }, 50);
+ }, 150);
},
three: function(callback){
setTimeout(function(){
call_order.push(3);
callback(null, 3,3);
- }, 15);
+ }, 50);
}
};
}
@@ -101,6 +101,36 @@ exports['auto'] = function(test){
});
};
+exports['auto petrify'] = function (test) {
+ var callOrder = [];
+ async.auto({
+ task1: ['task2', function (callback) {
+ setTimeout(function () {
+ callOrder.push('task1');
+ callback();
+ }, 100);
+ }],
+ task2: function (callback) {
+ setTimeout(function () {
+ callOrder.push('task2');
+ callback();
+ }, 200);
+ },
+ task3: ['task2', function (callback) {
+ callOrder.push('task3');
+ callback();
+ }],
+ task4: ['task1', 'task2', function (callback) {
+ callOrder.push('task4');
+ callback();
+ }]
+ },
+ function (err) {
+ test.same(callOrder, ['task2', 'task3', 'task1', 'task4']);
+ test.done();
+ });
+};
+
exports['auto results'] = function(test){
var callOrder = [];
async.auto({
@@ -363,6 +393,82 @@ exports['parallel object'] = function(test){
});
};
+exports['parallel limit'] = function(test){
+ var call_order = [];
+ async.parallelLimit([
+ function(callback){
+ setTimeout(function(){
+ call_order.push(1);
+ callback(null, 1);
+ }, 50);
+ },
+ function(callback){
+ setTimeout(function(){
+ call_order.push(2);
+ callback(null, 2);
+ }, 100);
+ },
+ function(callback){
+ setTimeout(function(){
+ call_order.push(3);
+ callback(null, 3,3);
+ }, 25);
+ }
+ ],
+ 2,
+ function(err, results){
+ test.equals(err, null);
+ test.same(call_order, [1,3,2]);
+ test.same(results, [1,2,[3,3]]);
+ test.done();
+ });
+};
+
+exports['parallel limit empty array'] = function(test){
+ async.parallelLimit([], 2, function(err, results){
+ test.equals(err, null);
+ test.same(results, []);
+ test.done();
+ });
+};
+
+exports['parallel limit error'] = function(test){
+ async.parallelLimit([
+ function(callback){
+ callback('error', 1);
+ },
+ function(callback){
+ callback('error2', 2);
+ }
+ ],
+ 1,
+ function(err, results){
+ test.equals(err, 'error');
+ });
+ setTimeout(test.done, 100);
+};
+
+exports['parallel limit no callback'] = function(test){
+ async.parallelLimit([
+ function(callback){callback();},
+ function(callback){callback(); test.done();},
+ ], 1);
+};
+
+exports['parallel limit object'] = function(test){
+ var call_order = [];
+ async.parallelLimit(getFunctionsObject(call_order), 2, function(err, results){
+ test.equals(err, null);
+ test.same(call_order, [1,3,2]);
+ test.same(results, {
+ one: 1,
+ two: 2,
+ three: [3,3]
+ });
+ test.done();
+ });
+};
+
exports['series'] = function(test){
var call_order = [];
async.series([
@@ -503,6 +609,18 @@ exports['forEach'] = function(test){
});
};
+exports['forEach extra callback'] = function(test){
+ var count = 0;
+ async.forEach([1,3,2], function(val, callback) {
+ count++;
+ callback();
+ test.throws(callback);
+ if (count == 3) {
+ test.done();
+ }
+ });
+};
+
exports['forEach empty array'] = function(test){
test.expect(1);
async.forEach([], function(x, callback){
@@ -622,7 +740,7 @@ exports['forEachLimit error'] = function(test){
test.expect(2);
var arr = [0,1,2,3,4,5,6,7,8,9];
var call_order = [];
-
+
async.forEachLimit(arr, 3, function(x, callback){
call_order.push(x);
if (x === 2) {
@@ -700,6 +818,75 @@ exports['mapSeries error'] = function(test){
setTimeout(test.done, 50);
};
+
+exports['mapLimit'] = function(test){
+ var call_order = [];
+ async.mapLimit([2,4,3], 2, mapIterator.bind(this, call_order), function(err, results){
+ test.same(call_order, [2,4,3]);
+ test.same(results, [4,8,6]);
+ test.done();
+ });
+};
+
+exports['mapLimit empty array'] = function(test){
+ test.expect(1);
+ async.mapLimit([], 2, 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['mapLimit limit exceeds size'] = function(test){
+ var call_order = [];
+ async.mapLimit([0,1,2,3,4,5,6,7,8,9], 20, mapIterator.bind(this, call_order), function(err, results){
+ test.same(call_order, [0,1,2,3,4,5,6,7,8,9]);
+ test.same(results, [0,2,4,6,8,10,12,14,16,18]);
+ test.done();
+ });
+};
+
+exports['mapLimit limit equal size'] = function(test){
+ var call_order = [];
+ async.mapLimit([0,1,2,3,4,5,6,7,8,9], 10, mapIterator.bind(this, call_order), function(err, results){
+ test.same(call_order, [0,1,2,3,4,5,6,7,8,9]);
+ test.same(results, [0,2,4,6,8,10,12,14,16,18]);
+ test.done();
+ });
+};
+
+exports['mapLimit zero limit'] = function(test){
+ test.expect(2);
+ async.mapLimit([0,1,2,3,4,5], 0, function(x, callback){
+ test.ok(false, 'iterator should not be called');
+ callback();
+ }, function(err, results){
+ test.same(results, []);
+ test.ok(true, 'should call callback');
+ });
+ setTimeout(test.done, 25);
+};
+
+exports['mapLimit error'] = function(test){
+ test.expect(2);
+ var arr = [0,1,2,3,4,5,6,7,8,9];
+ var call_order = [];
+
+ async.mapLimit(arr, 3, function(x, callback){
+ call_order.push(x);
+ if (x === 2) {
+ callback('error');
+ }
+ }, function(err){
+ test.same(call_order, [0,1,2]);
+ test.equals(err, 'error');
+ });
+ setTimeout(test.done, 25);
+};
+
+
exports['reduce'] = function(test){
var call_order = [];
async.reduce([1,2,3], 0, function(a, x, callback){
@@ -1183,6 +1370,34 @@ exports['until'] = function (test) {
);
};
+exports['doUntil'] = function (test) {
+ var call_order = [];
+ var count = 0;
+ async.doUntil(
+ function (cb) {
+ debugger
+ call_order.push(['iterator', count]);
+ count++;
+ cb();
+ },
+ function () {
+ call_order.push(['test', count]);
+ return (count == 5);
+ },
+ function (err) {
+ test.same(call_order, [
+ ['iterator', 0], ['test', 1],
+ ['iterator', 1], ['test', 2],
+ ['iterator', 2], ['test', 3],
+ ['iterator', 3], ['test', 4],
+ ['iterator', 4], ['test', 5]
+ ]);
+ test.equals(count, 5);
+ test.done();
+ }
+ );
+};
+
exports['whilst'] = function (test) {
var call_order = [];
@@ -1212,6 +1427,35 @@ exports['whilst'] = function (test) {
);
};
+exports['doWhilst'] = function (test) {
+ var call_order = [];
+
+ var count = 0;
+ async.doWhilst(
+ function (cb) {
+ call_order.push(['iterator', count]);
+ count++;
+ cb();
+ },
+ function () {
+ call_order.push(['test', count]);
+ return (count < 5);
+ },
+ function (err) {
+ debugger
+ test.same(call_order, [
+ ['iterator', 0], ['test', 1],
+ ['iterator', 1], ['test', 2],
+ ['iterator', 2], ['test', 3],
+ ['iterator', 3], ['test', 4],
+ ['iterator', 4], ['test', 5]
+ ]);
+ test.equals(count, 5);
+ test.done();
+ }
+ );
+};
+
exports['queue'] = function (test) {
var call_order = [],
delays = [160,80,240,80];
@@ -1254,7 +1498,7 @@ exports['queue'] = function (test) {
test.equal(q.length(), 4);
test.equal(q.concurrency, 2);
- setTimeout(function () {
+ q.drain = function () {
test.same(call_order, [
'process 2', 'callback 2',
'process 1', 'callback 1',
@@ -1264,7 +1508,7 @@ exports['queue'] = function (test) {
test.equal(q.concurrency, 2);
test.equal(q.length(), 0);
test.done();
- }, 800);
+ };
};
exports['queue changing concurrency'] = function (test) {
@@ -1353,6 +1597,18 @@ exports['queue push without callback'] = function (test) {
}, 800);
};
+exports['queue too many callbacks'] = function (test) {
+ var q = async.queue(function (task, callback) {
+ callback();
+ test.throws(function() {
+ callback();
+ });
+ test.done();
+ }, 2);
+
+ q.push(1);
+};
+
exports['queue bulk task'] = function (test) {
var call_order = [],
delays = [160,80,240,80];
@@ -1389,6 +1645,140 @@ exports['queue bulk task'] = function (test) {
}, 800);
};
+exports['cargo'] = function (test) {
+ var call_order = [],
+ delays = [160, 160, 80];
+
+ // worker: --12--34--5-
+ // order of completion: 1,2,3,4,5
+
+ var c = async.cargo(function (tasks, callback) {
+ setTimeout(function () {
+ call_order.push('process ' + tasks.join(' '));
+ callback('error', 'arg');
+ }, delays.shift());
+ }, 2);
+
+ c.push(1, function (err, arg) {
+ test.equal(err, 'error');
+ test.equal(arg, 'arg');
+ test.equal(c.length(), 3);
+ call_order.push('callback ' + 1);
+ });
+ c.push(2, function (err, arg) {
+ test.equal(err, 'error');
+ test.equal(arg, 'arg');
+ test.equal(c.length(), 3);
+ call_order.push('callback ' + 2);
+ });
+
+ test.equal(c.length(), 2);
+
+ // async push
+ setTimeout(function () {
+ c.push(3, function (err, arg) {
+ test.equal(err, 'error');
+ test.equal(arg, 'arg');
+ test.equal(c.length(), 1);
+ call_order.push('callback ' + 3);
+ });
+ }, 60);
+ setTimeout(function () {
+ c.push(4, function (err, arg) {
+ test.equal(err, 'error');
+ test.equal(arg, 'arg');
+ test.equal(c.length(), 1);
+ call_order.push('callback ' + 4);
+ });
+ test.equal(c.length(), 2);
+ c.push(5, function (err, arg) {
+ test.equal(err, 'error');
+ test.equal(arg, 'arg');
+ test.equal(c.length(), 0);
+ call_order.push('callback ' + 5);
+ });
+ }, 120);
+
+
+ setTimeout(function () {
+ test.same(call_order, [
+ 'process 1 2', 'callback 1', 'callback 2',
+ 'process 3 4', 'callback 3', 'callback 4',
+ 'process 5' , 'callback 5'
+ ]);
+ test.equal(c.length(), 0);
+ test.done();
+ }, 800);
+};
+
+exports['cargo without callback'] = function (test) {
+ var call_order = [],
+ delays = [160,80,240,80];
+
+ // worker: --1-2---34-5-
+ // order of completion: 1,2,3,4,5
+
+ var c = async.cargo(function (tasks, callback) {
+ setTimeout(function () {
+ call_order.push('process ' + tasks.join(' '));
+ callback('error', 'arg');
+ }, delays.shift());
+ }, 2);
+
+ c.push(1);
+
+ setTimeout(function () {
+ c.push(2);
+ }, 120);
+ setTimeout(function () {
+ c.push(3);
+ c.push(4);
+ c.push(5);
+ }, 180);
+
+ setTimeout(function () {
+ test.same(call_order, [
+ 'process 1',
+ 'process 2',
+ 'process 3 4',
+ 'process 5'
+ ]);
+ test.done();
+ }, 800);
+};
+
+exports['cargo bulk task'] = function (test) {
+ var call_order = [],
+ delays = [120,40];
+
+ // worker: -123-4-
+ // order of completion: 1,2,3,4
+
+ var c = async.cargo(function (tasks, callback) {
+ setTimeout(function () {
+ call_order.push('process ' + tasks.join(' '));
+ callback('error', tasks.join(' '));
+ }, delays.shift());
+ }, 3);
+
+ c.push( [1,2,3,4], function (err, arg) {
+ test.equal(err, 'error');
+ call_order.push('callback ' + arg);
+ });
+
+ test.equal(c.length(), 4);
+
+ setTimeout(function () {
+ test.same(call_order, [
+ 'process 1 2 3', 'callback 1 2 3',
+ 'callback 1 2 3', 'callback 1 2 3',
+ 'process 4', 'callback 4',
+ ]);
+ test.equal(c.length(), 0);
+ test.done();
+ }, 800);
+};
+
exports['memoize'] = function (test) {
test.expect(4);
var call_order = [];
@@ -1503,6 +1893,18 @@ exports['memoize custom hash function'] = function (test) {
test.done();
};
+exports['memoize manually added memo value'] = function (test) {
+ test.expect(1);
+ var fn = async.memoize(function(arg, callback) {
+ test(false, "Function should never be called");
+ });
+ fn.memo["foo"] = ["bar"];
+ fn("foo", function(val) {
+ test.equal(val, "bar");
+ test.done();
+ });
+};
+
// Issue 10 on github: https://github.com/caolan/async/issues#issue/10
exports['falsy return values in series'] = function (test) {
function taskFalse(callback) {
diff --git a/test/test-strict.js b/test/test-strict.js
new file mode 100644
index 0000000..39c14bf
--- /dev/null
+++ b/test/test-strict.js
@@ -0,0 +1,19 @@
+// run like this:
+// node --harmony --use-strict test-strict.js
+
+var async = require('../lib/async');
+
+function hi() {
+ let i = "abcd";
+ for (let i = 0; i < 3; i++) {
+ console.log(i);
+ }
+ console.log(i);
+}
+function hi2(){
+ console.log("blah");
+}
+
+async.parallel([hi, hi2], function() {
+ console.log("done");
+});