diff options
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | .npmignore | 5 | ||||
-rw-r--r-- | README.md | 140 | ||||
-rw-r--r-- | dist/async.min.js | 1 | ||||
-rw-r--r-- | index.js | 3 | ||||
-rw-r--r-- | lib/async.js | 227 | ||||
-rw-r--r-- | package.json | 42 | ||||
-rw-r--r-- | test/test-async.js | 414 | ||||
-rw-r--r-- | test/test-strict.js | 19 |
9 files changed, 773 insertions, 81 deletions
@@ -1 +1,2 @@ -node_modules +/node_modules +/dist @@ -1,4 +1,7 @@ deps dist test -nodelint.cfg
\ No newline at end of file +nodelint.cfg +.npmignore +.gitmodules +Makefile @@ -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"); +}); |