diff options
-rw-r--r-- | .jshintrc | 19 | ||||
-rw-r--r-- | .travis.yml | 2 | ||||
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | README.md | 136 | ||||
-rw-r--r-- | bower.json | 38 | ||||
-rw-r--r-- | component.json | 21 | ||||
-rw-r--r--[-rwxr-xr-x] | lib/async.js | 176 | ||||
-rw-r--r-- | package.json | 86 | ||||
-rwxr-xr-x | support/sync-package-managers.js | 53 | ||||
-rwxr-xr-x | test/test-async.js | 270 |
10 files changed, 681 insertions, 122 deletions
diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..3a2825a --- /dev/null +++ b/.jshintrc @@ -0,0 +1,19 @@ +{ + // Enforcing options + "eqeqeq": false, + "forin": true, + "indent": 4, + "noarg": true, + "undef": true, + "unused": true, + "trailing": true, + + // Relaxing options + "asi": false, + "eqnull": true, + "evil": true, + "expr": false, + "laxcomma": true, + "loopfunc": true, + "sub": true +} diff --git a/.travis.yml b/.travis.yml index 6e5919d..6064ca0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,5 @@ language: node_js node_js: - "0.10" + - "0.12" + - "iojs" @@ -1,6 +1,6 @@ PACKAGE = asyncjs NODEJS = $(if $(shell test -f /usr/bin/nodejs && echo "true"),nodejs,node) -CWD := $(shell pwd) +CWD := "$(shell pwd)" NODEUNIT = $(CWD)/node_modules/nodeunit/bin/nodeunit UGLIFY = $(CWD)/node_modules/uglify-js/bin/uglifyjs NODELINT = $(CWD)/node_modules/nodelint/nodelint @@ -1,12 +1,21 @@ # Async.js [![Build Status via Travis CI](https://travis-ci.org/caolan/async.svg?branch=master)](https://travis-ci.org/caolan/async) +[![NPM version](http://img.shields.io/npm/v/async.svg)](https://www.npmjs.org/package/async) Async is a utility module which provides straight-forward, powerful functions for working with asynchronous JavaScript. Although originally designed for -use with [Node.js](http://nodejs.org), it can also be used directly in the -browser. Also supports [component](https://github.com/component/component). +use with [Node.js](http://nodejs.org) and installable via `npm install async`, +it can also be used directly in the browser. + +Async is also installable via: + +- [bower](http://bower.io/): `bower install async` +- [component](https://github.com/component/component): `component install + caolan/async` +- [jam](http://jamjs.org/): `jam install async` +- [spm](http://spmjs.io/): `spm install async` Async provides around 20 functions that include the usual 'functional' suspects (`map`, `reduce`, `filter`, `each`…) as well as some common patterns @@ -115,6 +124,9 @@ Usage: * [`each`](#each) * [`eachSeries`](#eachSeries) * [`eachLimit`](#eachLimit) +* [`forEachOf`](#forEachOf) +* [`forEachOfSeries`](#forEachOfSeries) +* [`forEachOfLimit`](#forEachOfLimit) * [`map`](#map) * [`mapSeries`](#mapSeries) * [`mapLimit`](#mapLimit) @@ -187,7 +199,8 @@ __Arguments__ * `iterator(item, callback)` - A function to apply to each item in `arr`. The iterator is passed a `callback(err)` which must be called once it has completed. If no error has occurred, the `callback` should be run without - arguments or with an explicit `null` argument. + arguments or with an explicit `null` argument. The array index is not passed + to the iterator. If you need the index, use [`forEachOf`](#forEachOf). * `callback(err)` - A callback which is called when all `iterator` functions have finished, or an error occurs. @@ -206,7 +219,7 @@ async.each(openFiles, saveFile, function(err){ ```js // assuming openFiles is an array of file names -async.each(openFiles, function( file, callback) { +async.each(openFiles, function(file, callback) { // Perform operation on file here. console.log('Processing file ' + file); @@ -278,6 +291,67 @@ async.eachLimit(documents, 20, requestApi, function(err){ --------------------------------------- +<a name="forEachOf" /> +<a name="eachOf" /> + +### forEachOf(obj, iterator, callback) + +Like `each`, except that it iterates over objects, and passes the key as the second argument to the iterator. + +__Arguments__ + +* `obj` - An object or array to iterate over. +* `iterator(item, key, callback)` - A function to apply to each item in `obj`. +The `key` is the item's key, or index in the case of an array. The iterator is +passed a `callback(err)` which must be called once it has completed. If no +error has occurred, the callback should be run without arguments or with an +explicit `null` argument. +* `callback(err)` - A callback which is called when all `iterator` functions have finished, or an error occurs. + +__Example__ + +```js +var obj = {dev: "/dev.json", test: "/test.json", prod: "/prod.json"}; +var configs = {}; + +async.forEachOf(obj, function (value, key, callback) { + fs.readFile(__dirname + value, "utf8", function (err, data) { + if (err) return callback(err); + try { + configs[key] = JSON.parse(data); + } catch (e) { + return callback(e); + } + callback(); + }) +}, function (err) { + if (err) console.error(err.message); + // configs is now a map of JSON data + doSomethingWith(configs); +}) +``` + +--------------------------------------- + +<a name="forEachOfSeries" /> +<a name="eachOfSeries" /> + +### forEachOfSeries(obj, iterator, callback) + +Like [`forEachOf`](#forEachOf), except only one `iterator` is run at a time. The order of execution is not guaranteed for objects, but it will be guaranteed for arrays. + +--------------------------------------- + +<a name="forEachOfLimit" /> +<a name="eachOfLimit" /> + +### forEachOfLimit(obj, limit, iterator, callback) + +Like [`forEachOf`](#forEachOf), except the number of `iterator`s running at a given time is controlled by `limit`. + + +--------------------------------------- + <a name="map" /> ### map(arr, iterator, callback) @@ -540,14 +614,14 @@ By modifying the callback parameter the sorting order can be influenced: ```js //ascending order async.sortBy([1,9,3,5], function(x, callback){ - callback(err, x); + callback(null, x); }, function(err,result){ //result callback } ); //descending order async.sortBy([1,9,3,5], function(x, callback){ - callback(err, x*-1); //<- x*-1 instead of x, turns the order around + callback(null, x*-1); //<- x*-1 instead of x, turns the order around }, function(err,result){ //result callback } ); @@ -917,19 +991,19 @@ __Example__ ```js async.waterfall([ - function(callback){ + function(callback) { callback(null, 'one', 'two'); }, - function(arg1, arg2, callback){ + function(arg1, arg2, callback) { // arg1 now equals 'one' and arg2 now equals 'two' callback(null, 'three'); }, - function(arg1, callback){ + function(arg1, callback) { // arg1 now equals 'three' callback(null, 'done'); } ], function (err, result) { - // result now equals 'done' + // result now equals 'done' }); ``` @@ -976,7 +1050,8 @@ add1mul3(4, function (err, result) { ### seq(fn1, fn2...) Version of the compose function that is more natural to read. -Each following function consumes the return value of the latter function. +Each function consumes the return value of the previous function. +It is the equivalent of [`compose`](#compose) with the arguments reversed. Each function is executed with the `this` binding of the composed function. @@ -993,28 +1068,20 @@ __Example__ // This example uses `seq` function to avoid overnesting and error // handling clutter. app.get('/cats', function(request, response) { - function handleError(err, data, callback) { - if (err) { - console.error(err); - response.json({ status: 'error', message: err.message }); - } - else { - callback(data); - } - } var User = request.models.User; async.seq( _.bind(User.get, User), // 'User.get' has signature (id, callback(err, data)) - handleError, function(user, fn) { user.getCats(fn); // 'getCats' has signature (callback(err, data)) - }, - handleError, - function(cats) { + } + )(req.session.user_id, function (err, cats) { + if (err) { + console.error(err); + response.json({ status: 'error', message: err.message }); + } else { response.json({ status: 'ok', message: 'Cats found', data: cats }); } - )(req.session.user_id); - } + }); }); ``` @@ -1051,7 +1118,7 @@ async.each( --------------------------------------- <a name="applyEachSeries" /> -### applyEachSeries(arr, iterator, callback) +### applyEachSeries(arr, args..., callback) The same as [`applyEach`](#applyEach) only the functions are applied in series. @@ -1096,7 +1163,7 @@ methods: * `paused` - a boolean for determining whether the queue is in a paused state * `pause()` - a function that pauses the processing of tasks until `resume()` is called. * `resume()` - a function that resumes the processing of queued tasks when the queue is paused. -* `kill()` - a function that empties remaining tasks from the queue forcing it to go idle. +* `kill()` - a function that removes the `drain` callback and empties remaining tasks from the queue forcing it to go idle. __Example__ @@ -1126,7 +1193,7 @@ q.push({name: 'bar'}, function (err) { // add some items to the queue (batch-wise) q.push([{name: 'baz'},{name: 'bay'},{name: 'bax'}], function (err) { - console.log('finished processing bar'); + console.log('finished processing item'); }); // add some items to the front of the queue @@ -1353,7 +1420,7 @@ new tasks much easier (and the code more readable). Attempts to get a successful response from `task` no more than `times` times before returning an error. If the task is successful, the `callback` will be passed the result -of the successfull task. If all attemps fail, the callback will be passed the error and +of the successful task. If all attempts fail, the callback will be passed the error and result (if any) of the final attempt. __Arguments__ @@ -1478,7 +1545,7 @@ three --------------------------------------- <a name="nextTick" /> -### nextTick(callback) +### nextTick(callback), setImmediate(callback) Calls `callback` on a later loop around the event loop. In Node.js this just calls `process.nextTick`; in the browser it falls back to `setImmediate(callback)` @@ -1511,7 +1578,8 @@ you would use with [`map`](#map). __Arguments__ * `n` - The number of times to run the function. -* `callback` - The function to call `n` times. +* `iterator` - The function to call `n` times. +* `callback` - see [`map`](#map) __Example__ @@ -1549,13 +1617,15 @@ Caches the results of an `async` function. When creating a hash to store functio results against, the callback is omitted from the hash and an optional hash function can be used. +If no hash function is specified, the first argument is used as a hash key, which may work reasonably if it is a string or a data type that converts to a distinct string. Note that objects and arrays will not behave reasonably. Neither will cases where the other arguments are significant. In such cases, specify your own hash function. + The cache of results is exposed as the `memo` property of the function returned by `memoize`. __Arguments__ * `fn` - The function to proxy and cache results from. -* `hasher` - Tn optional function for generating a custom hash for storing +* `hasher` - An optional function for generating a custom hash for storing results. It has all the arguments applied to it apart from the callback, and must be synchronous. diff --git a/bower.json b/bower.json new file mode 100644 index 0000000..1817688 --- /dev/null +++ b/bower.json @@ -0,0 +1,38 @@ +{ + "name": "async", + "description": "Higher-order functions and common patterns for asynchronous code", + "version": "0.9.2", + "main": "lib/async.js", + "keywords": [ + "async", + "callback", + "utility", + "module" + ], + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/caolan/async.git" + }, + "devDependencies": { + "nodeunit": ">0.0.0", + "uglify-js": "1.2.x", + "nodelint": ">0.0.0", + "lodash": ">=2.4.1" + }, + "moduleType": [ + "amd", + "globals", + "node" + ], + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ], + "authors": [ + "Caolan McMahon" + ] +}
\ No newline at end of file diff --git a/component.json b/component.json index bbb0115..5003a7c 100644 --- a/component.json +++ b/component.json @@ -1,11 +1,16 @@ { "name": "async", - "repo": "caolan/async", "description": "Higher-order functions and common patterns for asynchronous code", - "version": "0.1.23", - "keywords": [], - "dependencies": {}, - "development": {}, - "main": "lib/async.js", - "scripts": [ "lib/async.js" ] -} + "version": "0.9.2", + "keywords": [ + "async", + "callback", + "utility", + "module" + ], + "license": "MIT", + "repository": "caolan/async", + "scripts": [ + "lib/async.js" + ] +}
\ No newline at end of file diff --git a/lib/async.js b/lib/async.js index a13f835..108bc9d 100755..100644 --- a/lib/async.js +++ b/lib/async.js @@ -10,11 +10,21 @@ (function () { var async = {}; + var noop = function () {}; // global on the server, window in the browser var root, previous_async; - root = this; + if (typeof window == 'object' && this === window) { + root = window; + } + else if (typeof global == 'object' && this === global) { + root = global; + } + else { + root = this; + } + if (root != null) { previous_async = root.async; } @@ -42,9 +52,6 @@ }; var _each = function (arr, iterator) { - if (arr.forEach) { - return arr.forEach(iterator); - } for (var i = 0; i < arr.length; i += 1) { iterator(arr[i], i, arr); } @@ -71,6 +78,14 @@ return memo; }; + var _forEachOf = function (object, iterator) { + for (key in object) { + if (object.hasOwnProperty(key)) { + iterator(object[key], key); + } + } + }; + var _keys = function (obj) { if (Object.keys) { return Object.keys(obj); @@ -116,7 +131,7 @@ } async.each = function (arr, iterator, callback) { - callback = callback || function () {}; + callback = callback || noop; if (!arr.length) { return callback(); } @@ -127,7 +142,7 @@ function done(err) { if (err) { callback(err); - callback = function () {}; + callback = noop; } else { completed += 1; @@ -140,7 +155,7 @@ async.forEach = async.each; async.eachSeries = function (arr, iterator, callback) { - callback = callback || function () {}; + callback = callback || noop; if (!arr.length) { return callback(); } @@ -149,7 +164,7 @@ iterator(arr[completed], function (err) { if (err) { callback(err); - callback = function () {}; + callback = noop; } else { completed += 1; @@ -166,6 +181,7 @@ }; async.forEachSeries = async.eachSeries; + async.eachLimit = function (arr, limit, iterator, callback) { var fn = _eachLimit(limit); fn.apply(null, [arr, iterator, callback]); @@ -175,7 +191,7 @@ var _eachLimit = function (limit) { return function (arr, iterator, callback) { - callback = callback || function () {}; + callback = callback || noop; if (!arr.length || limit <= 0) { return callback(); } @@ -194,7 +210,7 @@ iterator(arr[started - 1], function (err) { if (err) { callback(err); - callback = function () {}; + callback = noop; } else { completed += 1; @@ -213,6 +229,115 @@ }; + + async.forEachOf = async.eachOf = function (object, iterator, callback) { + callback = callback || function () {}; + var size = object.length || _keys(object).length; + var completed = 0 + if (!size) { + return callback(); + } + _forEachOf(object, function (value, key) { + iterator(object[key], key, function (err) { + if (err) { + callback(err); + callback = function () {}; + } else { + completed += 1; + if (completed === size) { + callback(null); + } + } + }); + }); + }; + + async.forEachOfSeries = async.eachOfSeries = function (obj, iterator, callback) { + callback = callback || function () {}; + var keys = _keys(obj); + var size = keys.length; + if (!size) { + return callback(); + } + var completed = 0; + var iterate = function () { + var sync = true; + var key = keys[completed]; + iterator(obj[key], key, function (err) { + if (err) { + callback(err); + callback = function () {}; + } + else { + completed += 1; + if (completed >= size) { + callback(null); + } + else { + if (sync) { + async.nextTick(iterate); + } + else { + iterate(); + } + } + } + }); + sync = false; + }; + iterate(); + }; + + + + async.forEachOfLimit = async.eachOfLimit = function (obj, limit, iterator, callback) { + _forEachOfLimit(limit)(obj, iterator, callback); + }; + + var _forEachOfLimit = function (limit) { + + return function (obj, iterator, callback) { + callback = callback || function () {}; + var keys = _keys(obj); + var size = keys.length; + if (!size || limit <= 0) { + return callback(); + } + var completed = 0; + var started = 0; + var running = 0; + + (function replenish () { + if (completed >= size) { + return callback(); + } + + while (running < limit && started < size) { + started += 1; + running += 1; + var key = keys[started - 1]; + iterator(obj[key], key, function (err) { + if (err) { + callback(err); + callback = function () {}; + } + else { + completed += 1; + running -= 1; + if (completed >= size) { + callback(); + } + else { + replenish(); + } + } + }); + } + })(); + }; + }; + + var doParallel = function (fn) { return function () { var args = Array.prototype.slice.call(arguments); @@ -345,7 +470,7 @@ iterator(x, function (result) { if (result) { main_callback(x); - main_callback = function () {}; + main_callback = noop; } else { callback(); @@ -363,7 +488,7 @@ iterator(x, function (v) { if (v) { main_callback(true); - main_callback = function () {}; + main_callback = noop; } callback(); }); @@ -379,7 +504,7 @@ iterator(x, function (v) { if (!v) { main_callback(false); - main_callback = function () {}; + main_callback = noop; } callback(); }); @@ -417,7 +542,7 @@ }; async.auto = function (tasks, callback) { - callback = callback || function () {}; + callback = callback || noop; var keys = _keys(tasks); var remainingTasks = keys.length if (!remainingTasks) { @@ -449,7 +574,7 @@ if (!remainingTasks) { var theCallback = callback; // prevent final callback from calling itself if it errors - callback = function () {}; + callback = noop; theCallback(null, results); } @@ -470,7 +595,7 @@ safeResults[k] = args; callback(err, safeResults); // stop subsequent errors hitting callback multiple times - callback = function () {}; + callback = noop; } else { results[k] = args; @@ -478,6 +603,17 @@ } }; var requires = task.slice(0, Math.abs(task.length - 1)) || []; + // prevent dead-locks + var len = requires.length; + var dep; + while (len--) { + if (!(dep = tasks[requires[len]])) { + throw new Error('Has inexistant dependency'); + } + if (_isArray(dep) && !!~dep.indexOf(k)) { + throw new Error('Has cyclic dependencies'); + } + } var ready = function () { return _reduce(requires, function (a, x) { return (a && results.hasOwnProperty(x)); @@ -530,7 +666,7 @@ }; async.waterfall = function (tasks, callback) { - callback = callback || function () {}; + callback = callback || noop; if (!_isArray(tasks)) { var err = new Error('First argument to waterfall must be an array of functions'); return callback(err); @@ -542,7 +678,7 @@ return function (err) { if (err) { callback.apply(null, arguments); - callback = function () {}; + callback = noop; } else { var args = Array.prototype.slice.call(arguments, 1); @@ -563,7 +699,7 @@ }; var _parallel = function(eachfn, tasks, callback) { - callback = callback || function () {}; + callback = callback || noop; if (_isArray(tasks)) { eachfn.map(tasks, function (fn, callback) { if (fn) { @@ -603,7 +739,7 @@ }; async.series = function (tasks, callback) { - callback = callback || function () {}; + callback = callback || noop; if (_isArray(tasks)) { async.mapSeries(tasks, function (fn, callback) { if (fn) { diff --git a/package.json b/package.json index 09f76aa..1424c76 100644 --- a/package.json +++ b/package.json @@ -1,36 +1,54 @@ { - "name": "async", - "description": "Higher-order functions and common patterns for asynchronous code", - "main": "./lib/async", - "author": "Caolan McMahon", - "version": "0.9.0", - "repository" : { - "type" : "git", - "url" : "https://github.com/caolan/async.git" - }, - "bugs" : { - "url" : "https://github.com/caolan/async/issues" - }, - "licenses" : [ - { - "type" : "MIT", - "url" : "https://github.com/caolan/async/raw/master/LICENSE" - } + "name": "async", + "description": "Higher-order functions and common patterns for asynchronous code", + "main": "lib/async.js", + "author": "Caolan McMahon", + "version": "0.9.2", + "keywords": [ + "async", + "callback", + "utility", + "module" + ], + "repository": { + "type": "git", + "url": "https://github.com/caolan/async.git" + }, + "bugs": { + "url": "https://github.com/caolan/async/issues" + }, + "license": "MIT", + "devDependencies": { + "nodeunit": ">0.0.0", + "uglify-js": "1.2.x", + "nodelint": ">0.0.0", + "lodash": ">=2.4.1" + }, + "jam": { + "main": "lib/async.js", + "include": [ + "lib/async.js", + "README.md", + "LICENSE" ], - "devDependencies": { - "nodeunit": ">0.0.0", - "uglify-js": "1.2.x", - "nodelint": ">0.0.0" - }, - "jam": { - "main": "lib/async.js", - "include": [ - "lib/async.js", - "README.md", - "LICENSE" - ] - }, - "scripts": { - "test": "nodeunit test/test-async.js" - } -} + "categories": [ + "Utilities" + ] + }, + "scripts": { + "test": "nodeunit test/test-async.js" + }, + "spm": { + "main": "lib/async.js" + }, + "volo": { + "main": "lib/async.js", + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ] + } +}
\ No newline at end of file diff --git a/support/sync-package-managers.js b/support/sync-package-managers.js new file mode 100755 index 0000000..30cb7c2 --- /dev/null +++ b/support/sync-package-managers.js @@ -0,0 +1,53 @@ +#!/usr/bin/env node + +// This should probably be its own module but complaints about bower/etc. +// support keep coming up and I'd rather just enable the workflow here for now +// and figure out where this should live later. -- @beaugunderson + +var fs = require('fs'); +var _ = require('lodash'); + +var packageJson = require('../package.json'); + +var IGNORES = ['**/.*', 'node_modules', 'bower_components', 'test', 'tests']; +var INCLUDES = ['lib/async.js', 'README.md', 'LICENSE']; +var REPOSITORY_NAME = 'caolan/async'; + +packageJson.jam = { + main: packageJson.main, + include: INCLUDES, + categories: ['Utilities'] +}; + +packageJson.spm = { + main: packageJson.main +}; + +packageJson.volo = { + main: packageJson.main, + ignore: IGNORES +}; + +var bowerSpecific = { + moduleType: ['amd', 'globals', 'node'], + ignore: IGNORES, + authors: [packageJson.author] +}; + +var bowerInclude = ['name', 'description', 'version', 'main', 'keywords', + 'license', 'homepage', 'repository', 'devDependencies']; + +var componentSpecific = { + repository: REPOSITORY_NAME, + scripts: [packageJson.main] +}; + +var componentInclude = ['name', 'description', 'version', 'keywords', + 'license']; + +var bowerJson = _.merge({}, _.pick(packageJson, bowerInclude), bowerSpecific); +var componentJson = _.merge({}, _.pick(packageJson, componentInclude), componentSpecific); + +fs.writeFileSync('./bower.json', JSON.stringify(bowerJson, null, 2)); +fs.writeFileSync('./component.json', JSON.stringify(componentJson, null, 2)); +fs.writeFileSync('./package.json', JSON.stringify(packageJson, null, 2)); diff --git a/test/test-async.js b/test/test-async.js index 6a35606..e660c1b 100755 --- a/test/test-async.js +++ b/test/test-async.js @@ -17,6 +17,13 @@ function eachIterator(args, x, callback) { }, x*25); } +function forEachOfIterator(args, value, key, callback) { + setTimeout(function(){ + args.push(key, value); + callback(); + }, value*25); +} + function mapIterator(call_order, x, callback) { setTimeout(function(){ call_order.push(x); @@ -43,6 +50,13 @@ function eachNoCallbackIterator(test, x, callback) { test.done(); } +function forEachOfNoCallbackIterator(test, x, key, callback) { + test.equal(x, 1); + test.equal(key, "a"); + callback(); + test.done(); +} + function getFunctionsObject(call_order) { return { one: function(callback){ @@ -580,6 +594,33 @@ exports['auto modifying results causes final callback to run early'] = function( }); }; +// Issue 263 on github: https://github.com/caolan/async/issues/263 +exports['auto prevent dead-locks due to inexistant dependencies'] = function(test) { + test.throws(function () { + async.auto({ + task1: ['noexist', function(callback, results){ + callback(null, 'task1'); + }] + }); + }, Error); + test.done(); +}; + +// Issue 263 on github: https://github.com/caolan/async/issues/263 +exports['auto prevent dead-locks due to cyclic dependencies'] = function(test) { + test.throws(function () { + async.auto({ + task1: ['task2', function(callback, results){ + callback(null, 'task1'); + }], + task2: ['task1', function(callback, results){ + callback(null, 'task2'); + }] + }); + }, Error); + test.done(); +}; + // Issue 306 on github: https://github.com/caolan/async/issues/306 exports['retry when attempt succeeds'] = function(test) { var failed = 3 @@ -619,7 +660,7 @@ exports['retry as an embedded task'] = function(test) { var retryResult = 'RETRY'; var fooResults; var retryResults; - + async.auto({ foo: function(callback, results){ fooResults = results; @@ -1165,6 +1206,47 @@ exports['forEach alias'] = function (test) { test.done(); }; +exports['forEachOf'] = function(test){ + var args = []; + async.forEachOf({ a: 1, b: 2 }, forEachOfIterator.bind(this, args), function(err){ + test.same(args, ["a", 1, "b", 2]); + test.done(); + }); +}; + +exports['forEachOf empty object'] = function(test){ + test.expect(1); + async.forEachOf({}, function(value, key, callback){ + test.ok(false, 'iterator should not be called'); + callback(); + }, function(err) { + test.ok(true, 'should call callback'); + }); + setTimeout(test.done, 25); +}; + +exports['forEachOf error'] = function(test){ + test.expect(1); + async.forEachOf({ a: 1, b: 2 }, function(value, key, callback) { + callback('error'); + }, function(err){ + test.equals(err, 'error'); + }); + setTimeout(test.done, 50); +}; + +exports['forEachOf no callback'] = function(test){ + async.forEachOf({ a: 1 }, forEachOfNoCallbackIterator.bind(this, test)); +}; + +exports['forEachOf with array'] = function(test){ + var args = []; + async.forEachOf([ "a", "b" ], forEachOfIterator.bind(this, args), function(err){ + test.same(args, [0, "a", 1, "b"]); + test.done(); + }); +}; + exports['eachSeries'] = function(test){ var args = []; async.eachSeries([1,3,2], eachIterator.bind(this, args), function(err){ @@ -1201,10 +1283,6 @@ exports['eachSeries no callback'] = function(test){ async.eachSeries([1], eachNoCallbackIterator.bind(this, test)); }; -exports['forEachSeries alias'] = function (test) { - test.strictEqual(async.eachSeries, async.forEachSeries); - test.done(); -}; exports['eachLimit'] = function(test){ var args = []; @@ -1293,11 +1371,153 @@ exports['eachLimit synchronous'] = function(test){ }); }; +exports['forEachSeries alias'] = function (test) { + test.strictEqual(async.eachSeries, async.forEachSeries); + test.done(); +}; + +exports['forEachOfSeries'] = function(test){ + var args = []; + async.forEachOfSeries({ a: 1, b: 2 }, forEachOfIterator.bind(this, args), function(err){ + test.same(args, [ "a", 1, "b", 2 ]); + test.done(); + }); +}; + +exports['forEachOfSeries empty object'] = function(test){ + test.expect(1); + async.forEachOfSeries({}, 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['forEachOfSeries error'] = function(test){ + test.expect(2); + var call_order = []; + async.forEachOfSeries({ a: 1, b: 2 }, function(value, key, callback){ + call_order.push(value, key); + callback('error'); + }, function(err){ + test.same(call_order, [ 1, "a" ]); + test.equals(err, 'error'); + }); + setTimeout(test.done, 50); +}; + +exports['forEachOfSeries no callback'] = function(test){ + async.forEachOfSeries({ a: 1 }, forEachOfNoCallbackIterator.bind(this, test)); +}; + +exports['forEachOfSeries with array'] = function(test){ + var args = []; + async.forEachOfSeries([ "a", "b" ], forEachOfIterator.bind(this, args), function(err){ + test.same(args, [ 0, "a", 1, "b" ]); + test.done(); + }); +}; + exports['forEachLimit alias'] = function (test) { test.strictEqual(async.eachLimit, async.forEachLimit); test.done(); }; +exports['forEachOfLimit'] = function(test){ + var args = []; + var obj = { a: 1, b: 2, c: 3, d: 4 }; + async.forEachOfLimit(obj, 2, function(value, key, callback){ + setTimeout(function(){ + args.push(value, key); + callback(); + }, value * 5); + }, function(err){ + test.same(args, [ 1, "a", 2, "b", 3, "c", 4, "d" ]); + test.done(); + }); +}; + +exports['forEachOfLimit empty object'] = function(test){ + test.expect(1); + async.forEachOfLimit({}, 2, function(value, key, callback){ + test.ok(false, 'iterator should not be called'); + callback(); + }, function(err){ + test.ok(true, 'should call callback'); + }); + setTimeout(test.done, 25); +}; + +exports['forEachOfLimit limit exceeds size'] = function(test){ + var args = []; + var obj = { a: 1, b: 2, c: 3, d: 4, e: 5 }; + async.forEachOfLimit(obj, 10, forEachOfIterator.bind(this, args), function(err){ + test.same(args, [ "a", 1, "b", 2, "c", 3, "d", 4, "e", 5 ]); + test.done(); + }); +}; + +exports['forEachOfLimit limit equal size'] = function(test){ + var args = []; + var obj = { a: 1, b: 2, c: 3, d: 4, e: 5 }; + async.forEachOfLimit(obj, 5, forEachOfIterator.bind(this, args), function(err){ + test.same(args, [ "a", 1, "b", 2, "c", 3, "d", 4, "e", 5 ]); + test.done(); + }); +}; + +exports['forEachOfLimit zero limit'] = function(test){ + test.expect(1); + async.forEachOfLimit({ a: 1, b: 2 }, 0, 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['forEachOfLimit error'] = function(test){ + test.expect(2); + var obj = { a: 1, b: 2, c: 3, d: 4, e: 5 }; + var call_order = []; + + async.forEachOfLimit(obj, 3, function(value, key, callback){ + call_order.push(value, key); + if (value === 2) { + callback('error'); + } + }, function(err){ + test.same(call_order, [ 1, "a", 2, "b" ]); + test.equals(err, 'error'); + }); + setTimeout(test.done, 25); +}; + +exports['forEachOfLimit no callback'] = function(test){ + async.forEachOfLimit({ a: 1 }, 1, forEachOfNoCallbackIterator.bind(this, test)); +}; + +exports['forEachOfLimit synchronous'] = function(test){ + var args = []; + var obj = { a: 1, b: 2 }; + async.forEachOfLimit(obj, 5, forEachOfIterator.bind(this, args), function(err){ + test.same(args, [ "a", 1, "b", 2 ]); + test.done(); + }); +}; + +exports['forEachOfLimit with array'] = function(test){ + var args = []; + var arr = [ "a", "b" ] + async.forEachOfLimit(arr, 1, forEachOfIterator.bind(this, args), function (err) { + test.same(args, [ 0, "a", 1, "b" ]); + test.done(); + }); +}; + exports['map'] = function(test){ var call_order = []; async.map([1,3,2], mapIterator.bind(this, call_order), function(err, results){ @@ -1882,15 +2102,13 @@ exports['noConflict - node only'] = function(test){ // node only test test.expect(3); var fs = require('fs'); + var vm = require('vm'); var filename = __dirname + '/../lib/async.js'; fs.readFile(filename, function(err, content){ if(err) return test.done(); - // Script -> NodeScript in node v0.6.x - var Script = process.binding('evals').Script || process.binding('evals').NodeScript; - - var s = new Script(content, filename); - var s2 = new Script( + var s = vm.createScript(content, filename); + var s2 = vm.createScript( content + 'this.async2 = this.async.noConflict();', filename ); @@ -1996,7 +2214,6 @@ exports['doUntil'] = function (test) { var count = 0; async.doUntil( function (cb) { - debugger call_order.push(['iterator', count]); count++; cb(); @@ -2024,7 +2241,6 @@ exports['doUntil callback params'] = function (test) { var count = 0; async.doUntil( function (cb) { - debugger call_order.push(['iterator', count]); count++; cb(null, count); @@ -2091,7 +2307,6 @@ exports['doWhilst'] = function (test) { return (count < 5); }, function (err) { - debugger test.same(call_order, [ ['iterator', 0], ['test', 1], ['iterator', 1], ['test', 2], @@ -2120,7 +2335,6 @@ exports['doWhilst callback params'] = function (test) { return (c < 5); }, function (err) { - debugger test.same(call_order, [ ['iterator', 0], ['test', 1], ['iterator', 1], ['test', 2], @@ -2459,8 +2673,10 @@ exports['queue pause'] = function(test) { tasks = [ 1, 2, 3, 4, 5, 6 ], elapsed = (function () { - var start = +Date.now(); - return function () { return Math.floor((+Date.now() - start) / 100) * 100; }; + var start = (new Date()).valueOf(); + return function () { + return Math.round(((new Date()).valueOf() - start) / 100) * 100; + }; })(); var q = async.queue(function (task, callback) { @@ -2510,8 +2726,10 @@ exports['queue pause with concurrency'] = function(test) { tasks = [ 1, 2, 3, 4, 5, 6 ], elapsed = (function () { - var start = +Date.now(); - return function () { return Math.floor((+Date.now() - start) / 100) * 100; }; + var start = (new Date()).valueOf(); + return function () { + return Math.round(((new Date()).valueOf() - start) / 100) * 100; + }; })(); var q = async.queue(function (task, callback) { @@ -2808,20 +3026,20 @@ exports['cargo bulk task'] = function (test) { }; exports['cargo drain once'] = function (test) { - + var c = async.cargo(function (tasks, callback) { callback(); }, 3); - + var drainCounter = 0; c.drain = function () { drainCounter++; } - + for(var i = 0; i < 10; i++){ c.push(i); } - + setTimeout(function(){ test.equal(drainCounter, 1); test.done(); @@ -2829,17 +3047,17 @@ exports['cargo drain once'] = function (test) { }; exports['cargo drain twice'] = function (test) { - + var c = async.cargo(function (tasks, callback) { callback(); }, 3); - + var loadCargo = function(){ for(var i = 0; i < 10; i++){ c.push(i); } }; - + var drainCounter = 0; c.drain = function () { drainCounter++; @@ -3163,7 +3381,7 @@ exports['queue started'] = function(test) { var calls = []; var q = async.queue(function(task, cb) {}); - + test.equal(q.started, false); q.push([]); test.equal(q.started, true); |