summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBao <baonhat.nguyen@gmail.com>2015-05-22 01:05:09 -0700
committerBao <baonhat.nguyen@gmail.com>2015-05-22 01:05:09 -0700
commit807546d534786c1cb79a3f2ae2d4fa9d4863e6d0 (patch)
tree002d735fc5db2c7344353131fce2b2d7cba7a2d6
parent11bf48b4ff52823edb66e35d05571724accc02d3 (diff)
parenta44d11ca95304dca673e6487050e630c2b2d87ee (diff)
downloadasync-807546d534786c1cb79a3f2ae2d4fa9d4863e6d0.tar.gz
Merge branch 'master' into fix/_eachLimit_continues_after_error
Conflicts: lib/async.js
-rw-r--r--.gitignore5
-rw-r--r--.jshintrc24
-rw-r--r--.npmignore2
-rw-r--r--.travis.yml3
-rw-r--r--CHANGELOG.md16
-rw-r--r--Makefile11
-rw-r--r--README.md104
-rw-r--r--bower.json12
-rw-r--r--component.json3
-rw-r--r--lib/async.js336
-rw-r--r--nodelint.cfg4
-rw-r--r--package.json19
-rwxr-xr-xperf/benchmark.js192
-rw-r--r--perf/suites.js209
-rwxr-xr-xsupport/sync-package-managers.js9
-rwxr-xr-xtest/test-async.js495
16 files changed, 1214 insertions, 230 deletions
diff --git a/.gitignore b/.gitignore
index 8225baa..291d97e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
-/node_modules
-/dist
+node_modules
+dist
+perf/versions
diff --git a/.jshintrc b/.jshintrc
new file mode 100644
index 0000000..172f491
--- /dev/null
+++ b/.jshintrc
@@ -0,0 +1,24 @@
+{
+ // Enforcing options
+ "eqeqeq": false,
+ "forin": true,
+ "indent": 4,
+ "noarg": true,
+ "undef": true,
+ "trailing": true,
+ "evil": true,
+ "laxcomma": true,
+
+ // Relaxing options
+ "onevar": false,
+ "asi": false,
+ "eqnull": true,
+ "expr": false,
+ "loopfunc": true,
+ "sub": true,
+ "browser": true,
+ "node": true,
+ "globals": {
+ "define": true
+ }
+}
diff --git a/.npmignore b/.npmignore
index 392d66e..5fc10cf 100644
--- a/.npmignore
+++ b/.npmignore
@@ -1,7 +1,7 @@
deps
dist
test
-nodelint.cfg
+perf
.npmignore
.gitmodules
Makefile
diff --git a/.travis.yml b/.travis.yml
index 05d299e..6064ca0 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,4 +1,5 @@
language: node_js
node_js:
- "0.10"
- - "0.11"
+ - "0.12"
+ - "iojs"
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..7d39c37
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,16 @@
+# v1.0.0
+
+No known breaking changes, we are simply complying with semver from here on out.
+
+Changes:
+
+- Start using a changelog!
+- Add `forEachOf` for iterating over Objects (or to iterate Arrays with indexes available) (#168 #704 #321)
+- Detect deadlocks in `auto` (#663)
+- Better support for require.js (#527)
+- Throw if queue created with concurrency `0` (#714)
+- Fix unneeded iteration in `queue.resume()` (#758)
+- Guard against timer mocking overriding `setImmediate` (#609 #611)
+- Miscellaneous doc fixes (#542 #596 #615 #628 #631 #690 #729)
+- Use single noop function internally (#546)
+- Optimize internal `_each`, `_map` and `_keys` functions.
diff --git a/Makefile b/Makefile
index bad647c..4356239 100644
--- a/Makefile
+++ b/Makefile
@@ -1,9 +1,8 @@
PACKAGE = asyncjs
-NODEJS = $(if $(shell test -f /usr/bin/nodejs && echo "true"),nodejs,node)
CWD := $(shell pwd)
-NODEUNIT = $(CWD)/node_modules/nodeunit/bin/nodeunit
-UGLIFY = $(CWD)/node_modules/uglify-js/bin/uglifyjs
-NODELINT = $(CWD)/node_modules/nodelint/nodelint
+NODEUNIT = "$(CWD)/node_modules/.bin/nodeunit"
+UGLIFY = "$(CWD)/node_modules/.bin/uglifyjs"
+JSHINT = "$(CWD)/node_modules/.bin/jshint"
BUILDDIR = dist
@@ -20,6 +19,6 @@ clean:
rm -rf $(BUILDDIR)
lint:
- $(NODELINT) --config nodelint.cfg lib/async.js
+ $(JSHINT) lib/*.js test/*.js perf/*.js
-.PHONY: test build all
+.PHONY: test lint build all clean
diff --git a/README.md b/README.md
index 6cfb922..4e0b9fc 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,7 @@
# 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
@@ -88,11 +89,15 @@ async.map([1, 2, 3], AsyncSquaringLibrary.square.bind(AsyncSquaringLibrary), fun
## Download
The source is available for download from
-[GitHub](http://github.com/caolan/async).
+[GitHub](https://github.com/caolan/async/blob/master/lib/async.js).
Alternatively, you can install using Node Package Manager (`npm`):
npm install async
+As well as using Bower:
+
+ bower install async
+
__Development:__ [async.js](https://github.com/caolan/async/raw/master/lib/async.js) - 29.6kb Uncompressed
## In the Browser
@@ -119,6 +124,9 @@ Usage:
* [`each`](#each)
* [`eachSeries`](#eachSeries)
* [`eachLimit`](#eachLimit)
+* [`forEachOf`](#forEachOf)
+* [`forEachOfSeries`](#forEachOfSeries)
+* [`forEachOfLimit`](#forEachOfLimit)
* [`map`](#map)
* [`mapSeries`](#mapSeries)
* [`mapLimit`](#mapLimit)
@@ -191,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.
@@ -282,13 +291,74 @@ 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)
Produces a new array of values by mapping each value in `arr` through
the `iterator` function. The `iterator` is called with an item from `arr` and a
callback for when it has finished processing. Each of these callback takes 2 arguments:
-an `error`, and the transformed item from `arr`. If `iterator` passes an error to his
+an `error`, and the transformed item from `arr`. If `iterator` passes an error to its
callback, the main `callback` (for the `map` function) is immediately called with the error.
Note, that since this function applies the `iterator` to each item in parallel,
@@ -482,11 +552,11 @@ __Arguments__
* `arr` - An array to iterate over.
* `iterator(item, callback)` - A truth test to apply to each item in `arr`.
The iterator is passed a `callback(truthValue)` which must be called with a
- boolean argument once it has completed.
+ boolean argument once it has completed. **Note: this callback does not take an error as its first argument.**
* `callback(result)` - A callback which is called as soon as any iterator returns
`true`, or after all the `iterator` functions have finished. Result will be
the first item in the array that passes the truth test (iterator) or the
- value `undefined` if none passed.
+ value `undefined` if none passed. **Note: this callback does not take an error as its first argument.**
__Example__
@@ -574,12 +644,13 @@ __Arguments__
* `arr` - An array to iterate over.
* `iterator(item, callback)` - A truth test to apply to each item in the array
- in parallel. The iterator is passed a callback(truthValue) which must be
+ in parallel. The iterator is passed a `callback(truthValue)`` which must be
called with a boolean argument once it has completed.
* `callback(result)` - A callback which is called as soon as any iterator returns
`true`, or after all the iterator functions have finished. Result will be
either `true` or `false` depending on the values of the async tests.
+ **Note: the callbacks do not take an error as their first argument.**
__Example__
```js
@@ -604,12 +675,14 @@ __Arguments__
* `arr` - An array to iterate over.
* `iterator(item, callback)` - A truth test to apply to each item in the array
- in parallel. The iterator is passed a callback(truthValue) which must be
+ in parallel. The iterator is passed a `callback(truthValue)` which must be
called with a boolean argument once it has completed.
* `callback(result)` - A callback which is called after all the `iterator`
functions have finished. Result will be either `true` or `false` depending on
the values of the async tests.
+ **Note: the callbacks do not take an error as their first argument.**
+
__Example__
```js
@@ -1048,14 +1121,14 @@ 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.
---------------------------------------
<a name="queue" />
-### queue(worker, concurrency)
+### queue(worker, [concurrency])
Creates a `queue` object with the specified `concurrency`. Tasks added to the
`queue` are processed in parallel (up to the `concurrency` limit). If all
@@ -1066,9 +1139,9 @@ __Arguments__
* `worker(task, callback)` - An asynchronous function for processing a queued
task, which must call its `callback(err)` argument when finished, with an
- optional `error` as an argument.
+ optional `error` as an argument. If you want to handle errors from an individual task, pass a callback to `q.push()`.
* `concurrency` - An `integer` for determining how many `worker` functions should be
- run in parallel.
+ run in parallel. If omitted, the concurrency defaults to `1`. If the concurrency is `0`, an error is thrown.
__Queue objects__
@@ -1154,7 +1227,7 @@ 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 becomes available. Once
the `worker` has completed some tasks, each callback of those tasks is called.
-Check out [this animation](https://camo.githubusercontent.com/6bbd36f4cf5b35a0f11a96dcd2e97711ffc2fb37/68747470733a2f2f662e636c6f75642e6769746875622e636f6d2f6173736574732f313637363837312f36383130382f62626330636662302d356632392d313165322d393734662d3333393763363464633835382e676966) for how `cargo` and `queue` work.
+Check out [these](https://camo.githubusercontent.com/6bbd36f4cf5b35a0f11a96dcd2e97711ffc2fb37/68747470733a2f2f662e636c6f75642e6769746875622e636f6d2f6173736574732f313637363837312f36383130382f62626330636662302d356632392d313165322d393734662d3333393763363464633835382e676966) [animations](https://camo.githubusercontent.com/f4810e00e1c5f5f8addbe3e9f49064fd5d102699/68747470733a2f2f662e636c6f75642e6769746875622e636f6d2f6173736574732f313637363837312f36383130312f38346339323036362d356632392d313165322d383134662d3964336430323431336266642e676966) for how `cargo` and `queue` work.
While [queue](#queue) passes only one task to one of a group of workers
at a time, cargo passes an array of tasks to a single worker, repeating
@@ -1508,7 +1581,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__
@@ -1546,13 +1620,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
index e6f1b42..9e4156d 100644
--- a/bower.json
+++ b/bower.json
@@ -1,7 +1,7 @@
{
"name": "async",
"description": "Higher-order functions and common patterns for asynchronous code",
- "version": "0.9.2",
+ "version": "1.0.0",
"main": "lib/async.js",
"keywords": [
"async",
@@ -9,22 +9,24 @@
"utility",
"module"
],
+ "license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/caolan/async.git"
},
"devDependencies": {
+ "benchmark": "~1.0.0",
+ "jshint": "~2.7.0",
+ "lodash": ">=2.4.1",
+ "mkdirp": "~0.5.1",
"nodeunit": ">0.0.0",
- "uglify-js": "1.2.x",
- "nodelint": ">0.0.0",
- "lodash": ">=2.4.1"
+ "uglify-js": "1.2.x"
},
"moduleType": [
"amd",
"globals",
"node"
],
- "license": "MIT",
"ignore": [
"**/.*",
"node_modules",
diff --git a/component.json b/component.json
index 5003a7c..c876b0a 100644
--- a/component.json
+++ b/component.json
@@ -1,7 +1,7 @@
{
"name": "async",
"description": "Higher-order functions and common patterns for asynchronous code",
- "version": "0.9.2",
+ "version": "1.0.0",
"keywords": [
"async",
"callback",
@@ -9,6 +9,7 @@
"module"
],
"license": "MIT",
+ "main": "lib/async.js",
"repository": "caolan/async",
"scripts": [
"lib/async.js"
diff --git a/lib/async.js b/lib/async.js
index b93e178..4257f0d 100644
--- a/lib/async.js
+++ b/lib/async.js
@@ -5,16 +5,24 @@
* Copyright 2010-2014 Caolan McMahon
* Released under the MIT license
*/
-/*jshint onevar: false, indent:4 */
-/*global setImmediate: false, setTimeout: false, console: false */
(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;
}
@@ -30,7 +38,7 @@
if (called) throw new Error("Callback was already called.");
called = true;
fn.apply(root, arguments);
- }
+ };
}
//// cross-browser compatiblity functions ////
@@ -42,36 +50,39 @@
};
var _each = function (arr, iterator) {
- for (var i = 0; i < arr.length; i += 1) {
- iterator(arr[i], i, arr);
- }
+ var index = -1,
+ length = arr.length;
+
+ while (++index < length) {
+ iterator(arr[index], index, arr);
+ }
};
var _map = function (arr, iterator) {
- if (arr.map) {
- return arr.map(iterator);
- }
- var results = [];
- _each(arr, function (x, i, a) {
- results.push(iterator(x, i, a));
- });
- return results;
+ var index = -1,
+ length = arr.length,
+ result = Array(length);
+
+ while (++index < length) {
+ result[index] = iterator(arr[index], index, arr);
+ }
+ return result;
};
var _reduce = function (arr, iterator, memo) {
- if (arr.reduce) {
- return arr.reduce(iterator, memo);
- }
_each(arr, function (x, i, a) {
memo = iterator(memo, x, i, a);
});
return memo;
};
- var _keys = function (obj) {
- if (Object.keys) {
- return Object.keys(obj);
- }
+ var _forEachOf = function (object, iterator) {
+ _each(_keys(object), function (key) {
+ iterator(object[key], key);
+ });
+ };
+
+ var _keys = Object.keys || function (obj) {
var keys = [];
for (var k in obj) {
if (obj.hasOwnProperty(k)) {
@@ -81,14 +92,38 @@
return keys;
};
+ var _baseSlice = function (arr, start) {
+ start = start || 0;
+ var index = -1;
+ var length = arr.length;
+
+ if (start) {
+ length -= start;
+ length = length < 0 ? 0 : length;
+ }
+ var result = Array(length);
+
+ while (++index < length) {
+ result[index] = arr[index + start];
+ }
+ return result;
+ };
+
//// exported async module functions ////
//// nextTick implementation with browser-compatible fallback ////
+
+ // capture the global reference to guard against fakeTimer mocks
+ var _setImmediate;
+ if (typeof setImmediate === 'function') {
+ _setImmediate = setImmediate;
+ }
+
if (typeof process === 'undefined' || !(process.nextTick)) {
- if (typeof setImmediate === 'function') {
+ if (_setImmediate) {
async.nextTick = function (fn) {
// not a direct alias for IE10 compatibility
- setImmediate(fn);
+ _setImmediate(fn);
};
async.setImmediate = async.nextTick;
}
@@ -101,10 +136,10 @@
}
else {
async.nextTick = process.nextTick;
- if (typeof setImmediate !== 'undefined') {
+ if (_setImmediate) {
async.setImmediate = function (fn) {
// not a direct alias for IE10 compatibility
- setImmediate(fn);
+ _setImmediate(fn);
};
}
else {
@@ -113,9 +148,9 @@
}
async.each = function (arr, iterator, callback) {
- callback = callback || function () {};
+ callback = callback || noop;
if (!arr.length) {
- return callback();
+ return callback(null);
}
var completed = 0;
_each(arr, function (x) {
@@ -124,12 +159,12 @@
function done(err) {
if (err) {
callback(err);
- callback = function () {};
+ callback = noop;
}
else {
completed += 1;
if (completed >= arr.length) {
- callback();
+ callback(null);
}
}
}
@@ -137,21 +172,21 @@
async.forEach = async.each;
async.eachSeries = function (arr, iterator, callback) {
- callback = callback || function () {};
+ callback = callback || noop;
if (!arr.length) {
- return callback();
+ return callback(null);
}
var completed = 0;
var iterate = function () {
iterator(arr[completed], function (err) {
if (err) {
callback(err);
- callback = function () {};
+ callback = noop;
}
else {
completed += 1;
if (completed >= arr.length) {
- callback();
+ callback(null);
}
else {
iterate();
@@ -163,6 +198,7 @@
};
async.forEachSeries = async.eachSeries;
+
async.eachLimit = function (arr, limit, iterator, callback) {
var fn = _eachLimit(limit);
fn.apply(null, [arr, iterator, callback]);
@@ -172,9 +208,9 @@
var _eachLimit = function (limit) {
return function (arr, iterator, callback) {
- callback = callback || function () {};
+ callback = callback || noop;
if (!arr.length || limit <= 0) {
- return callback();
+ return callback(null);
}
var completed = 0;
var started = 0;
@@ -183,7 +219,7 @@
(function replenish () {
if (completed >= arr.length) {
- return callback();
+ return callback(null);
}
while (running < limit && started < arr.length && !errored) {
@@ -192,14 +228,123 @@
iterator(arr[started - 1], function (err) {
if (err) {
callback(err);
- callback = function () {};
errored = true;
+ callback = noop;
}
else {
completed += 1;
running -= 1;
if (completed >= arr.length) {
- callback();
+ callback(null);
+ }
+ else {
+ replenish();
+ }
+ }
+ });
+ }
+ })();
+ };
+ };
+
+
+
+ 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(null);
+ }
+ _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(null);
+ }
+ 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(null);
}
else {
replenish();
@@ -214,19 +359,19 @@
var doParallel = function (fn) {
return function () {
- var args = Array.prototype.slice.call(arguments);
+ var args = _baseSlice(arguments);
return fn.apply(null, [async.each].concat(args));
};
};
var doParallelLimit = function(limit, fn) {
return function () {
- var args = Array.prototype.slice.call(arguments);
+ var args = _baseSlice(arguments);
return fn.apply(null, [_eachLimit(limit)].concat(args));
};
};
var doSeries = function (fn) {
return function () {
- var args = Array.prototype.slice.call(arguments);
+ var args = _baseSlice(arguments);
return fn.apply(null, [async.eachSeries].concat(args));
};
};
@@ -273,7 +418,7 @@
callback(err);
});
}, function (err) {
- callback(err, memo);
+ callback(err || null, memo);
});
};
// inject alias
@@ -344,7 +489,7 @@
iterator(x, function (result) {
if (result) {
main_callback(x);
- main_callback = function () {};
+ main_callback = noop;
}
else {
callback();
@@ -362,7 +507,7 @@
iterator(x, function (v) {
if (v) {
main_callback(true);
- main_callback = function () {};
+ main_callback = noop;
}
callback();
});
@@ -378,7 +523,7 @@
iterator(x, function (v) {
if (!v) {
main_callback(false);
- main_callback = function () {};
+ main_callback = noop;
}
callback();
});
@@ -416,11 +561,11 @@
};
async.auto = function (tasks, callback) {
- callback = callback || function () {};
+ callback = callback || noop;
var keys = _keys(tasks);
- var remainingTasks = keys.length
+ var remainingTasks = keys.length;
if (!remainingTasks) {
- return callback();
+ return callback(null);
}
var results = {};
@@ -438,7 +583,7 @@
}
};
var taskComplete = function () {
- remainingTasks--
+ remainingTasks--;
_each(listeners.slice(0), function (fn) {
fn();
});
@@ -448,7 +593,7 @@
if (!remainingTasks) {
var theCallback = callback;
// prevent final callback from calling itself if it errors
- callback = function () {};
+ callback = noop;
theCallback(null, results);
}
@@ -457,7 +602,7 @@
_each(keys, function (k) {
var task = _isArray(tasks[k]) ? tasks[k]: [tasks[k]];
var taskCallback = function (err) {
- var args = Array.prototype.slice.call(arguments, 1);
+ var args = _baseSlice(arguments, 1);
if (args.length <= 1) {
args = args[0];
}
@@ -469,7 +614,7 @@
safeResults[k] = args;
callback(err, safeResults);
// stop subsequent errors hitting callback multiple times
- callback = function () {};
+ callback = noop;
}
else {
results[k] = args;
@@ -477,6 +622,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));
@@ -523,13 +679,13 @@
data = data[data.length - 1];
(wrappedCallback || callback)(data.err, data.result);
});
- }
+ };
// If a callback is passed, run this as a controll flow
- return callback ? wrappedTask() : wrappedTask
+ return callback ? wrappedTask() : wrappedTask;
};
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);
@@ -541,10 +697,10 @@
return function (err) {
if (err) {
callback.apply(null, arguments);
- callback = function () {};
+ callback = noop;
}
else {
- var args = Array.prototype.slice.call(arguments, 1);
+ var args = _baseSlice(arguments, 1);
var next = iterator.next();
if (next) {
args.push(wrapIterator(next));
@@ -562,12 +718,12 @@
};
var _parallel = function(eachfn, tasks, callback) {
- callback = callback || function () {};
+ callback = callback || noop;
if (_isArray(tasks)) {
eachfn.map(tasks, function (fn, callback) {
if (fn) {
fn(function (err) {
- var args = Array.prototype.slice.call(arguments, 1);
+ var args = _baseSlice(arguments, 1);
if (args.length <= 1) {
args = args[0];
}
@@ -580,7 +736,7 @@
var results = {};
eachfn.each(_keys(tasks), function (k, callback) {
tasks[k](function (err) {
- var args = Array.prototype.slice.call(arguments, 1);
+ var args = _baseSlice(arguments, 1);
if (args.length <= 1) {
args = args[0];
}
@@ -602,12 +758,12 @@
};
async.series = function (tasks, callback) {
- callback = callback || function () {};
+ callback = callback || noop;
if (_isArray(tasks)) {
async.mapSeries(tasks, function (fn, callback) {
if (fn) {
fn(function (err) {
- var args = Array.prototype.slice.call(arguments, 1);
+ var args = _baseSlice(arguments, 1);
if (args.length <= 1) {
args = args[0];
}
@@ -620,7 +776,7 @@
var results = {};
async.eachSeries(_keys(tasks), function (k, callback) {
tasks[k](function (err) {
- var args = Array.prototype.slice.call(arguments, 1);
+ var args = _baseSlice(arguments, 1);
if (args.length <= 1) {
args = args[0];
}
@@ -650,10 +806,10 @@
};
async.apply = function (fn) {
- var args = Array.prototype.slice.call(arguments, 1);
+ var args = _baseSlice(arguments, 1);
return function () {
return fn.apply(
- null, args.concat(Array.prototype.slice.call(arguments))
+ null, args.concat(_baseSlice(arguments))
);
};
};
@@ -682,7 +838,7 @@
});
}
else {
- callback();
+ callback(null);
}
};
@@ -691,12 +847,12 @@
if (err) {
return callback(err);
}
- var args = Array.prototype.slice.call(arguments, 1);
+ var args = _baseSlice(arguments, 1);
if (test.apply(null, args)) {
async.doWhilst(iterator, test, callback);
}
else {
- callback();
+ callback(null);
}
});
};
@@ -711,7 +867,7 @@
});
}
else {
- callback();
+ callback(null);
}
};
@@ -720,12 +876,12 @@
if (err) {
return callback(err);
}
- var args = Array.prototype.slice.call(arguments, 1);
+ var args = _baseSlice(arguments, 1);
if (!test.apply(null, args)) {
async.doUntil(iterator, test, callback);
}
else {
- callback();
+ callback(null);
}
});
};
@@ -734,6 +890,9 @@
if (concurrency === undefined) {
concurrency = 1;
}
+ else if(concurrency === 0) {
+ throw new Error('Concurrency must not be zero');
+ }
function _insert(q, data, pos, callback) {
if (!q.started){
q.started = true;
@@ -741,7 +900,7 @@
if (!_isArray(data)) {
data = [data];
}
- if(data.length == 0) {
+ if(data.length === 0) {
// call drain immediately if there are no tasks
return async.setImmediate(function() {
if (q.drain) {
@@ -824,9 +983,10 @@
resume: function () {
if (q.paused === false) { return; }
q.paused = false;
+ var resumeCount = Math.min(q.concurrency, q.tasks.length);
// Need to call q.process once per concurrent
// worker to preserve full concurrency after pause
- for (var w = 1; w <= q.concurrency; w++) {
+ for (var w = 1; w <= resumeCount; w++) {
async.setImmediate(q.process);
}
}
@@ -838,7 +998,7 @@
function _compareTasks(a, b){
return a.priority - b.priority;
- };
+ }
function _binarySearch(sequence, item, compare) {
var beg = -1,
@@ -861,7 +1021,7 @@
if (!_isArray(data)) {
data = [data];
}
- if(data.length == 0) {
+ if(data.length === 0) {
// call drain immediately if there are no tasks
return async.setImmediate(function() {
if (q.drain) {
@@ -934,9 +1094,9 @@
return;
}
- var ts = typeof payload === 'number'
- ? tasks.splice(0, payload)
- : tasks.splice(0, tasks.length);
+ var ts = typeof payload === 'number' ?
+ tasks.splice(0, payload) :
+ tasks.splice(0, tasks.length);
var ds = _map(ts, function (task) {
return task.data;
@@ -969,9 +1129,9 @@
var _console_fn = function (name) {
return function (fn) {
- var args = Array.prototype.slice.call(arguments, 1);
+ var args = _baseSlice(arguments, 1);
fn.apply(null, args.concat([function (err) {
- var args = Array.prototype.slice.call(arguments, 1);
+ var args = _baseSlice(arguments, 1);
if (typeof console !== 'undefined') {
if (err) {
if (console.error) {
@@ -1000,7 +1160,7 @@
return x;
};
var memoized = function () {
- var args = Array.prototype.slice.call(arguments);
+ var args = _baseSlice(arguments);
var callback = args.pop();
var key = hasher.apply(null, args);
if (key in memo) {
@@ -1014,7 +1174,7 @@
else {
queues[key] = [callback];
fn.apply(null, args.concat([function () {
- memo[key] = arguments;
+ memo[key] = _baseSlice(arguments);
var q = queues[key];
delete queues[key];
for (var i = 0, l = q.length; i < l; i++) {
@@ -1054,14 +1214,14 @@
var fns = arguments;
return function () {
var that = this;
- var args = Array.prototype.slice.call(arguments);
+ var args = _baseSlice(arguments);
var callback = args.pop();
async.reduce(fns, args, function (newargs, fn, cb) {
fn.apply(that, newargs.concat([function () {
var err = arguments[0];
- var nextargs = Array.prototype.slice.call(arguments, 1);
+ var nextargs = _baseSlice(arguments, 1);
cb(err, nextargs);
- }]))
+ }]));
},
function (err, results) {
callback.apply(that, [err].concat(results));
@@ -1076,7 +1236,7 @@
var _applyEach = function (eachfn, fns /*args...*/) {
var go = function () {
var that = this;
- var args = Array.prototype.slice.call(arguments);
+ var args = _baseSlice(arguments);
var callback = args.pop();
return eachfn(fns, function (fn, cb) {
fn.apply(that, args.concat([cb]));
@@ -1084,7 +1244,7 @@
callback);
};
if (arguments.length > 2) {
- var args = Array.prototype.slice.call(arguments, 2);
+ var args = _baseSlice(arguments, 2);
return go.apply(this, args);
}
else {
diff --git a/nodelint.cfg b/nodelint.cfg
deleted file mode 100644
index 457a967..0000000
--- a/nodelint.cfg
+++ /dev/null
@@ -1,4 +0,0 @@
-var options = {
- indent: 4,
- onevar: false
-};
diff --git a/package.json b/package.json
index f5debe2..6e7282b 100644
--- a/package.json
+++ b/package.json
@@ -3,7 +3,7 @@
"description": "Higher-order functions and common patterns for asynchronous code",
"main": "lib/async.js",
"author": "Caolan McMahon",
- "version": "0.9.2",
+ "version": "1.0.0",
"keywords": [
"async",
"callback",
@@ -17,15 +17,15 @@
"bugs": {
"url": "https://github.com/caolan/async/issues"
},
- "license": {
- "type": "MIT",
- "url": "https://github.com/caolan/async/raw/master/LICENSE"
- },
+ "license": "MIT",
"devDependencies": {
+ "benchmark": "bestiejs/benchmark.js",
+ "jshint": "~2.7.0",
+ "lodash": ">=2.4.1",
+ "mkdirp": "~0.5.1",
"nodeunit": ">0.0.0",
"uglify-js": "1.2.x",
- "nodelint": ">0.0.0",
- "lodash": ">=2.4.1"
+ "yargs": "~3.9.1"
},
"jam": {
"main": "lib/async.js",
@@ -39,7 +39,8 @@
]
},
"scripts": {
- "test": "nodeunit test/test-async.js"
+ "test": "npm run-script lint && nodeunit test/test-async.js",
+ "lint": "jshint lib/*.js test/*.js perf/*.js"
},
"spm": {
"main": "lib/async.js"
@@ -54,4 +55,4 @@
"tests"
]
}
-} \ No newline at end of file
+}
diff --git a/perf/benchmark.js b/perf/benchmark.js
new file mode 100755
index 0000000..da59216
--- /dev/null
+++ b/perf/benchmark.js
@@ -0,0 +1,192 @@
+#!/usr/bin/env node
+
+var _ = require("lodash");
+var Benchmark = require("benchmark");
+var benchOptions = {defer: true, minSamples: 1, maxTime: 2};
+var exec = require("child_process").exec;
+var fs = require("fs");
+var path = require("path");
+var mkdirp = require("mkdirp");
+var async = require("../");
+var suiteConfigs = require("./suites");
+
+var args = require("yargs")
+ .usage("Usage: $0 [options] [tag1] [tag2]")
+ .describe("g", "run only benchmarks whose names match this regex")
+ .alias("g", "grep")
+ .default("g", ".*")
+ .describe("i", "skip benchmarks whose names match this regex")
+ .alias("g", "reject")
+ .default("i", "^$")
+ .help('h')
+ .alias('h', 'help')
+ .example('$0 0.9.2 0.9.0', 'Compare v0.9.2 with v0.9.0')
+ .example('$0 0.9.2', 'Compare v0.9.2 with the current working version')
+ .example('$0', 'Compare the latest tag with the current working version')
+ .example('$0 -g each', 'only run the each(), eachLimit() and eachSeries() benchmarks')
+ .example('')
+ .argv;
+
+var grep = new RegExp(args.g, "i");
+var reject = new RegExp(args.i, "i");
+
+var version0 = args._[0] || require("../package.json").version;
+var version1 = args._[1] || "current";
+var versionNames = [version0, version1];
+var versions;
+var wins = {};
+var totalTime = {};
+totalTime[version0] = wins[version0] = 0;
+totalTime[version1] = wins[version1] = 0;
+
+console.log("Comparing " + version0 + " with " + version1);
+console.log("--------------------------------------");
+
+
+async.eachSeries(versionNames, cloneVersion, function (err) {
+ versions = versionNames.map(requireVersion);
+
+ var suites = suiteConfigs
+ .map(setDefaultOptions)
+ .reduce(handleMultipleArgs, [])
+ .map(setName)
+ .filter(matchesGrep)
+ .filter(doesNotMatch)
+ .map(createSuite);
+
+ async.eachSeries(suites, runSuite, function () {
+ var totalTime0 = +totalTime[version0].toPrecision(3);
+ var totalTime1 = +totalTime[version1].toPrecision(3);
+
+ var wins0 = wins[version0];
+ var wins1 = wins[version1];
+
+ if ( Math.abs((totalTime0 / totalTime1) - 1) < 0.01) {
+ // if < 1% difference, we're likely within the margins of error
+ console.log("Both versions are about equal " +
+ "(" + totalTime0 + "ms total vs. " + totalTime1 + "ms total)");
+ } else if (totalTime0 < totalTime1) {
+ console.log(version0 + " faster overall " +
+ "(" + totalTime0 + "ms total vs. " + totalTime1 + "ms total)");
+ } else if (totalTime1 < totalTime0) {
+ console.log(version1 + " faster overall " +
+ "(" + totalTime1 + "ms total vs. " + totalTime0 + "ms total)");
+ }
+
+ if (wins0 > wins1) {
+ console.log(version0 + " won more benchmarks " +
+ "(" + wins0 + " vs. " + wins1 + ")");
+ } else if (wins1 > wins0) {
+ console.log(version1 + " won more benchmarks " +
+ "(" + wins1 + " vs. " + wins0 + ")");
+ } else {
+ console.log("Both versions won the same number of benchmarks " +
+ "(" + wins0 + " vs. " + wins1 + ")");
+ }
+ });
+});
+
+function runSuite(suite, callback) {
+ suite.on("complete", function () {
+ callback();
+ }).run({async: true});
+}
+
+function setDefaultOptions(suiteConfig) {
+ suiteConfig.args = suiteConfig.args || [[]];
+ suiteConfig.setup = suiteConfig.setup || function () {};
+ return suiteConfig;
+}
+
+function handleMultipleArgs(list, suiteConfig) {
+ return list.concat(suiteConfig.args.map(function (args) {
+ return _.defaults({args: args}, suiteConfig);
+ }));
+}
+
+function setName(suiteConfig) {
+ suiteConfig.name = suiteConfig.name + "(" + suiteConfig.args.join(",") + ")";
+ return suiteConfig;
+}
+
+function matchesGrep(suiteConfig) {
+ return !!grep.exec(suiteConfig.name);
+}
+
+function doesNotMatch(suiteConfig) {
+ return !reject.exec(suiteConfig.name);
+}
+
+function createSuite(suiteConfig) {
+ var suite = new Benchmark.Suite();
+ var args = suiteConfig.args;
+
+ function addBench(version, versionName) {
+ var name = suiteConfig.name + " " + versionName;
+ suite.add(name, function (deferred) {
+ suiteConfig.fn(version, function () {
+ deferred.resolve();
+ });
+ }, _.extend({
+ versionName: versionName,
+ setup: _.partial.apply(null, [suiteConfig.setup].concat(args))
+ }, benchOptions));
+ }
+
+ addBench(versions[0], versionNames[0]);
+ addBench(versions[1], versionNames[1]);
+
+
+ return suite.on('cycle', function(event) {
+ var mean = event.target.stats.mean * 1000;
+ console.log(event.target + ", " + (+mean.toPrecision(2)) + "ms per run");
+ var version = event.target.options.versionName;
+ totalTime[version] += mean;
+ })
+ .on('complete', function() {
+ var fastest = this.filter('fastest');
+ if (fastest.length === 2) {
+ console.log("Tie");
+ } else {
+ var winner = fastest[0].options.versionName;
+ console.log(winner + ' is faster');
+ wins[winner]++;
+ }
+ console.log("--------------------------------------");
+ });
+
+}
+
+function requireVersion(tag) {
+ if (tag === "current") {
+ return async;
+ }
+
+ return require("./versions/" + tag + "/");
+}
+
+function cloneVersion(tag, callback) {
+ if (tag === "current") return callback();
+
+ var versionDir = __dirname + "/versions/" + tag;
+ mkdirp.sync(versionDir);
+ fs.open(versionDir + "/package.json", "r", function (err, handle) {
+ if (!err) {
+ // version has already been cloned
+ fs.close(handle);
+ return callback();
+ }
+
+ var repoPath = path.join(__dirname, "..");
+
+ var cmd = "git clone --branch " + tag + " " + repoPath + " " + versionDir;
+
+ exec(cmd, function (err, stdout, stderr) {
+ if (err) {
+ throw err;
+ }
+ callback();
+ });
+
+ });
+}
diff --git a/perf/suites.js b/perf/suites.js
new file mode 100644
index 0000000..9a36db5
--- /dev/null
+++ b/perf/suites.js
@@ -0,0 +1,209 @@
+var _ = require("lodash");
+var tasks;
+
+module.exports = [
+ {
+ name: "each",
+ // args lists are passed to the setup function
+ args: [[10], [300], [10000]],
+ setup: function(count) {
+ tasks = _.range(count);
+ },
+ fn: function (async, done) {
+ async.each(tasks, function (num, cb) {
+ async.setImmediate(cb);
+ }, done);
+ }
+ },
+ {
+ name: "eachSeries",
+ args: [[10], [300], [10000]],
+ setup: function(count) {
+ tasks = _.range(count);
+ },
+ fn: function (async, done) {
+ async.eachSeries(tasks, function (num, cb) {
+ async.setImmediate(cb);
+ }, done);
+ }
+ },
+ {
+ name: "eachLimit",
+ args: [[10], [300], [10000]],
+ setup: function(count) {
+ tasks = _.range(count);
+ },
+ fn: function (async, done) {
+ async.eachLimit(tasks, 4, function (num, cb) {
+ async.setImmediate(cb);
+ }, done);
+ }
+ },
+ {
+ name: "map",
+ // args lists are passed to the setup function
+ args: [[10], [300], [10000]],
+ setup: function(count) {
+ tasks = _.range(count);
+ },
+ fn: function (async, done) {
+ async.map(tasks, function (num, cb) {
+ async.setImmediate(cb);
+ }, done);
+ }
+ },
+ {
+ name: "mapSeries",
+ args: [[10], [300], [10000]],
+ setup: function(count) {
+ tasks = _.range(count);
+ },
+ fn: function (async, done) {
+ async.mapSeries(tasks, function (num, cb) {
+ async.setImmediate(cb);
+ }, done);
+ }
+ },
+ {
+ name: "mapLimit",
+ args: [[10], [300], [10000]],
+ setup: function(count) {
+ tasks = _.range(count);
+ },
+ fn: function (async, done) {
+ async.mapLimit(tasks, 4, function (num, cb) {
+ async.setImmediate(cb);
+ }, done);
+ }
+ },
+ {
+ name: "eachOf",
+ // args lists are passed to the setup function
+ args: [[10], [300], [10000]],
+ setup: function(count) {
+ tasks = _.range(count);
+ },
+ fn: function (async, done) {
+ async.eachOf(tasks, function (num, i, cb) {
+ async.setImmediate(cb);
+ }, done);
+ }
+ },
+ {
+ name: "eachOfSeries",
+ args: [[10], [300], [10000]],
+ setup: function(count) {
+ tasks = _.range(count);
+ },
+ fn: function (async, done) {
+ async.eachOfSeries(tasks, function (num, i, cb) {
+ async.setImmediate(cb);
+ }, done);
+ }
+ },
+ {
+ name: "eachOfLimit",
+ args: [[10], [300], [10000]],
+ setup: function(count) {
+ tasks = _.range(count);
+ },
+ fn: function (async, done) {
+ async.eachOfLimit(tasks, 4, function (num, i, cb) {
+ async.setImmediate(cb);
+ }, done);
+ }
+ },
+ {
+ name: "parallel",
+ args: [[10], [100], [1000]],
+ setup: function (count) {
+ tasks = _.range(count).map(function () {
+ return function (cb) {
+ setImmediate(cb);
+ };
+ });
+ },
+ fn: function (async, done) {
+ async.parallel(tasks, done);
+ }
+ },
+ {
+ name: "series",
+ args: [[10], [100], [1000]],
+ setup: function (count) {
+ tasks = _.range(count).map(function () {
+ return function (cb) { setImmediate(cb); };
+ });
+ },
+ fn: function (async, done) {
+ async.series(tasks, done);
+ }
+ },
+ {
+ name: "waterfall",
+ args: [[10], [100], [1000]],
+ setup: function (count) {
+ tasks = [
+ function (cb) {
+ return cb(null, 1);
+ }
+ ].concat(_.range(count).map(function (i) {
+ return function (arg, cb) { cb(null, i); };
+ }));
+ },
+ fn: function (async, done) {
+ async.waterfall(tasks, done);
+ }
+ },
+ {
+ name: "queue",
+ args: [[1000], [30000], [100000], [200000]],
+ setup: function (count) {
+ tasks = count;
+ },
+ fn: function (async, done) {
+ var numEntries = tasks;
+ var q = async.queue(worker, 1);
+ for (var i = 1; i <= numEntries; i++) {
+ q.push({num: i});
+ }
+ function worker(task, callback) {
+ if (task.num === numEntries) {
+ return done();
+ }
+ setImmediate(callback);
+ }
+ }
+ },
+ {
+ name: "defer nextTick",
+ fn: function (async, done) {
+ process.nextTick(done);
+ }
+ },
+ {
+ name: "defer setImmediate",
+ fn: function (async, done) {
+ setImmediate(done);
+ }
+ },
+ {
+ name: "defer async.nextTick",
+ fn: function (async, done) {
+ async.nextTick(done);
+ }
+ },
+ {
+ name: "defer async.setImmediate",
+ fn: function (async, done) {
+ async.setImmediate(done);
+ }
+ },
+ {
+ name: "defer setTimeout",
+ fn: function (async, done) {
+ setTimeout(done, 0);
+ }
+ }
+];
+
diff --git a/support/sync-package-managers.js b/support/sync-package-managers.js
index 310bbe6..5b26119 100755
--- a/support/sync-package-managers.js
+++ b/support/sync-package-managers.js
@@ -13,8 +13,6 @@ var IGNORES = ['**/.*', 'node_modules', 'bower_components', 'test', 'tests'];
var INCLUDES = ['lib/async.js', 'README.md', 'LICENSE'];
var REPOSITORY_NAME = 'caolan/async';
-var LICENSE_NAME = packageJson.license.type;
-
packageJson.jam = {
main: packageJson.main,
include: INCLUDES,
@@ -32,21 +30,20 @@ packageJson.volo = {
var bowerSpecific = {
moduleType: ['amd', 'globals', 'node'],
- license: LICENSE_NAME,
ignore: IGNORES,
authors: [packageJson.author]
};
var bowerInclude = ['name', 'description', 'version', 'main', 'keywords',
- 'homepage', 'repository', 'devDependencies'];
+ 'license', 'homepage', 'repository', 'devDependencies'];
var componentSpecific = {
- license: LICENSE_NAME,
repository: REPOSITORY_NAME,
scripts: [packageJson.main]
};
-var componentInclude = ['name', 'description', 'version', 'keywords'];
+var componentInclude = ['name', 'description', 'version', 'keywords',
+ 'license', 'main'];
var bowerJson = _.merge({}, _.pick(packageJson, bowerInclude), bowerSpecific);
var componentJson = _.merge({}, _.pick(packageJson, componentInclude), componentSpecific);
diff --git a/test/test-async.js b/test/test-async.js
index 2d46b8c..1cb860e 100755
--- a/test/test-async.js
+++ b/test/test-async.js
@@ -6,7 +6,7 @@ if (!Function.prototype.bind) {
var self = this;
return function () {
self.apply(thisArg, args.concat(Array.prototype.slice.call(arguments)));
- }
+ };
};
}
@@ -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){
@@ -85,7 +99,7 @@ exports['forever'] = function (test) {
};
exports['applyEach'] = function (test) {
- test.expect(4);
+ test.expect(5);
var call_order = [];
var one = function (val, cb) {
test.equal(val, 5);
@@ -109,13 +123,14 @@ exports['applyEach'] = function (test) {
}, 150);
};
async.applyEach([one, two, three], 5, function (err) {
+ test.ok(err === null, err + " passed instead of 'null'");
test.same(call_order, ['two', 'one', 'three']);
test.done();
});
};
exports['applyEachSeries'] = function (test) {
- test.expect(4);
+ test.expect(5);
var call_order = [];
var one = function (val, cb) {
test.equal(val, 5);
@@ -139,6 +154,7 @@ exports['applyEachSeries'] = function (test) {
}, 150);
};
async.applyEachSeries([one, two, three], 5, function (err) {
+ test.ok(err === null, err + " passed instead of 'null'");
test.same(call_order, ['one', 'two', 'three']);
test.done();
});
@@ -175,7 +191,7 @@ exports['applyEach partial application'] = function (test) {
};
exports['compose'] = function (test) {
- test.expect(4);
+ test.expect(5);
var add2 = function (n, cb) {
test.equal(n, 3);
setTimeout(function () {
@@ -199,6 +215,7 @@ exports['compose'] = function (test) {
if (err) {
return test.done(err);
}
+ test.ok(err === null, err + " passed instead of 'null'");
test.equal(result, 16);
test.done();
});
@@ -262,7 +279,7 @@ exports['compose binding'] = function (test) {
};
exports['seq'] = function (test) {
- test.expect(4);
+ test.expect(5);
var add2 = function (n, cb) {
test.equal(n, 3);
setTimeout(function () {
@@ -286,6 +303,7 @@ exports['seq'] = function (test) {
if (err) {
return test.done(err);
}
+ test.ok(err === null, err + " passed instead of 'null'");
test.equal(result, 16);
test.done();
});
@@ -384,6 +402,7 @@ exports['auto'] = function(test){
}]
},
function(err){
+ test.ok(err === null, err + " passed instead of 'null'");
test.same(callOrder, ['task2','task6','task3','task5','task1','task4']);
test.done();
});
@@ -457,6 +476,7 @@ exports['auto results'] = function(test){
exports['auto empty object'] = function(test){
async.auto({}, function(err){
+ test.ok(err === null, err + " passed instead of 'null'");
test.done();
});
};
@@ -512,7 +532,7 @@ exports['auto error should pass partial results'] = function(test) {
// Issue 76 on github: https://github.com/caolan/async/issues#issue/76
exports['auto removeListener has side effect on loop iterator'] = function(test) {
async.auto({
- task1: ['task3', function(callback) { test.done() }],
+ task1: ['task3', function(callback) { test.done(); }],
task2: ['task3', function(callback) { /* by design: DON'T call callback */ }],
task3: function(callback) { callback(); }
});
@@ -559,7 +579,7 @@ exports['auto calls callback multiple times'] = function(test) {
exports['auto modifying results causes final callback to run early'] = function(test) {
async.auto({
task1: function(callback, results){
- results.inserted = true
+ results.inserted = true;
callback(null, 'task1');
},
task2: function(callback){
@@ -574,26 +594,54 @@ exports['auto modifying results causes final callback to run early'] = function(
}
},
function(err, results){
- test.equal(results.inserted, true)
- test.ok(results.task3, 'task3')
+ test.equal(results.inserted, true);
+ test.ok(results.task3, 'task3');
test.done();
});
};
+// 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
- var callCount = 0
- var expectedResult = 'success'
+ var failed = 3;
+ var callCount = 0;
+ var expectedResult = 'success';
function fn(callback, results) {
- callCount++
- failed--
- if (!failed) callback(null, expectedResult)
- else callback(true) // respond with error
+ callCount++;
+ failed--;
+ if (!failed) callback(null, expectedResult);
+ else callback(true); // respond with error
}
async.retry(fn, function(err, result){
- test.equal(callCount, 3, 'did not retry the correct number of times')
- test.equal(result, expectedResult, 'did not return the expected result')
+ test.ok(err === null, err + " passed instead of 'null'");
+ test.equal(callCount, 3, 'did not retry the correct number of times');
+ test.equal(result, expectedResult, 'did not return the expected result');
test.done();
});
};
@@ -606,7 +654,7 @@ exports['retry when all attempts succeeds'] = function(test) {
function fn(callback, results) {
callCount++;
callback(error + callCount, erroredResult + callCount); // respond with indexed values
- };
+ }
async.retry(times, fn, function(err, result){
test.equal(callCount, 3, "did not retry the correct number of times");
test.equal(err, error + times, "Incorrect error was returned");
@@ -619,7 +667,7 @@ exports['retry as an embedded task'] = function(test) {
var retryResult = 'RETRY';
var fooResults;
var retryResults;
-
+
async.auto({
foo: function(callback, results){
fooResults = results;
@@ -636,8 +684,10 @@ exports['retry as an embedded task'] = function(test) {
});
};
-exports['waterfall'] = function(test){
- test.expect(6);
+exports['waterfall'] = {
+
+'basic': function(test){
+ test.expect(7);
var call_order = [];
async.waterfall([
function(callback){
@@ -663,31 +713,32 @@ exports['waterfall'] = function(test){
callback(null, 'test');
}
], function(err){
+ test.ok(err === null, err + " passed instead of 'null'");
test.done();
});
-};
+},
-exports['waterfall empty array'] = function(test){
+'empty array': function(test){
async.waterfall([], function(err){
test.done();
});
-};
+},
-exports['waterfall non-array'] = function(test){
+'non-array': function(test){
async.waterfall({}, function(err){
test.equals(err.message, 'First argument to waterfall must be an array of functions');
test.done();
});
-};
+},
-exports['waterfall no callback'] = function(test){
+'no callback': function(test){
async.waterfall([
function(callback){callback();},
function(callback){callback(); test.done();}
]);
-};
+},
-exports['waterfall async'] = function(test){
+'async': function(test){
var call_order = [];
async.waterfall([
function(callback){
@@ -704,9 +755,9 @@ exports['waterfall async'] = function(test){
test.done();
}
]);
-};
+},
-exports['waterfall error'] = function(test){
+'error': function(test){
test.expect(1);
async.waterfall([
function(callback){
@@ -720,9 +771,9 @@ exports['waterfall error'] = function(test){
test.equals(err, 'error');
});
setTimeout(test.done, 50);
-};
+},
-exports['waterfall multiple callback calls'] = function(test){
+'multiple callback calls': function(test){
var call_order = [];
var arr = [
function(callback){
@@ -749,9 +800,9 @@ exports['waterfall multiple callback calls'] = function(test){
}
];
async.waterfall(arr);
-};
+},
-exports['waterfall call in another context'] = function(test) {
+'call in another context': function(test) {
if (typeof process === 'undefined') {
// node only test
test.done();
@@ -776,8 +827,9 @@ exports['waterfall call in another context'] = function(test) {
}).toString() + "())";
vm.runInNewContext(fn, sandbox);
-};
+}
+};
exports['parallel'] = function(test){
var call_order = [];
@@ -802,7 +854,7 @@ exports['parallel'] = function(test){
}
],
function(err, results){
- test.equals(err, null);
+ test.ok(err === null, err + " passed instead of 'null'");
test.same(call_order, [3,1,2]);
test.same(results, [1,2,[3,3]]);
test.done();
@@ -811,7 +863,7 @@ exports['parallel'] = function(test){
exports['parallel empty array'] = function(test){
async.parallel([], function(err, results){
- test.equals(err, null);
+ test.ok(err === null, err + " passed instead of 'null'");
test.same(results, []);
test.done();
});
@@ -877,7 +929,7 @@ exports['parallel limit'] = function(test){
],
2,
function(err, results){
- test.equals(err, null);
+ test.ok(err === null, err + " passed instead of 'null'");
test.same(call_order, [1,3,2]);
test.same(results, [1,2,[3,3]]);
test.done();
@@ -886,7 +938,7 @@ exports['parallel limit'] = function(test){
exports['parallel limit empty array'] = function(test){
async.parallelLimit([], 2, function(err, results){
- test.equals(err, null);
+ test.ok(err === null, err + " passed instead of 'null'");
test.same(results, []);
test.done();
});
@@ -979,7 +1031,7 @@ exports['series'] = function(test){
}
],
function(err, results){
- test.equals(err, null);
+ test.ok(err === null, err + " passed instead of 'null'");
test.same(results, [1,2,[3,3]]);
test.same(call_order, [1,2,3]);
test.done();
@@ -1118,6 +1170,7 @@ exports['iterator.next'] = function(test){
exports['each'] = function(test){
var args = [];
async.each([1,3,2], eachIterator.bind(this, args), function(err){
+ test.ok(err === null, err + " passed instead of 'null'");
test.same(args, [1,2,3]);
test.done();
});
@@ -1165,9 +1218,52 @@ 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.ok(err === null, err + " passed instead of 'null'");
+ 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){
+ test.ok(err === null, err + " passed instead of 'null'");
test.same(args, [1,3,2]);
test.done();
});
@@ -1201,10 +1297,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 = [];
@@ -1215,6 +1307,7 @@ exports['eachLimit'] = function(test){
callback();
}, x*5);
}, function(err){
+ test.ok(err === null, err + " passed instead of 'null'");
test.same(args, arr);
test.done();
});
@@ -1293,14 +1386,159 @@ 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.ok(err === null, err + " passed instead of 'null'");
+ 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.ok(err === null, err + " passed instead of 'null'");
+ 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){
+ test.ok(err === null, err + " passed instead of 'null'");
test.same(call_order, [1,2,3]);
test.same(results, [2,6,4]);
test.done();
@@ -1344,6 +1582,7 @@ exports['map error'] = function(test){
exports['mapSeries'] = function(test){
var call_order = [];
async.mapSeries([1,3,2], mapIterator.bind(this, call_order), function(err, results){
+ test.ok(err === null, err + " passed instead of 'null'");
test.same(call_order, [1,3,2]);
test.same(results, [2,6,4]);
test.done();
@@ -1364,6 +1603,7 @@ exports['mapSeries error'] = function(test){
exports['mapLimit'] = function(test){
var call_order = [];
async.mapLimit([2,4,3], 2, mapIterator.bind(this, call_order), function(err, results){
+ test.ok(err === null, err + " passed instead of 'null'");
test.same(call_order, [2,4,3]);
test.same(results, [4,8,6]);
test.done();
@@ -1435,6 +1675,7 @@ exports['reduce'] = function(test){
call_order.push(x);
callback(null, a + x);
}, function(err, result){
+ test.ok(err === null, err + " passed instead of 'null'");
test.equals(result, 6);
test.same(call_order, [1,2,3]);
test.done();
@@ -1443,7 +1684,7 @@ exports['reduce'] = function(test){
exports['reduce async with non-reference memo'] = function(test){
async.reduce([1,3,2], 0, function(a, x, callback){
- setTimeout(function(){callback(null, a + x)}, Math.random()*100);
+ setTimeout(function(){callback(null, a + x);}, Math.random()*100);
}, function(err, result){
test.equals(result, 6);
test.done();
@@ -1675,10 +1916,21 @@ exports['detectSeries - multiple matches'] = function(test){
}, 200);
};
+exports['detectSeries - ensure stop'] = function (test) {
+ async.detectSeries([1, 2, 3, 4, 5], function (num, cb) {
+ if (num > 3) throw new Error("detectSeries did not stop iterating");
+ cb(num === 3);
+ }, function (result) {
+ test.equals(result, 3);
+ test.done();
+ });
+};
+
exports['sortBy'] = function(test){
async.sortBy([{a:1},{a:15},{a:6}], function(x, callback){
setTimeout(function(){callback(null, x.a);}, 0);
}, function(err, result){
+ test.ok(err === null, err + " passed instead of 'null'");
test.same(result, [{a:1},{a:6},{a:15}]);
test.done();
});
@@ -1696,7 +1948,7 @@ exports['sortBy inverted'] = function(test){
exports['apply'] = function(test){
test.expect(6);
var fn = function(){
- test.same(Array.prototype.slice.call(arguments), [1,2,3,4])
+ test.same(Array.prototype.slice.call(arguments), [1,2,3,4]);
};
async.apply(fn, 1, 2, 3, 4)();
async.apply(fn, 1, 2, 3)(4);
@@ -1704,7 +1956,7 @@ exports['apply'] = function(test){
async.apply(fn, 1)(2, 3, 4);
async.apply(fn)(1, 2, 3, 4);
test.equals(
- async.apply(function(name){return 'hello ' + name}, 'world')(),
+ async.apply(function(name){return 'hello ' + name;}, 'world')(),
'hello world'
);
test.done();
@@ -1774,14 +2026,15 @@ var console_fn_tests = function(name){
exports['times'] = function(test) {
- var indices = []
+ var indices = [];
async.times(5, function(n, next) {
- next(null, n)
+ next(null, n);
}, function(err, results) {
- test.same(results, [0,1,2,3,4])
- test.done()
- })
-}
+ test.ok(err === null, err + " passed instead of 'null'");
+ test.same(results, [0,1,2,3,4]);
+ test.done();
+ });
+};
exports['times'] = function(test){
var args = [];
@@ -1869,9 +2122,6 @@ exports['nextTick in the browser'] = function(test){
call_order.push('one');
setTimeout(function(){
- if (typeof process !== 'undefined') {
- process.nextTick = _nextTick;
- }
test.same(call_order, ['one','two']);
}, 50);
setTimeout(test.done, 100);
@@ -1924,7 +2174,7 @@ exports['concat'] = function(test){
async.concat([1,3,2], iterator, function(err, results){
test.same(results, [1,2,1,3,2,1]);
test.same(call_order, [1,2,3]);
- test.ok(!err);
+ test.ok(err === null, err + " passed instead of 'null'");
test.done();
});
};
@@ -1955,7 +2205,7 @@ exports['concatSeries'] = function(test){
async.concatSeries([1,3,2], iterator, function(err, results){
test.same(results, [1,3,2,1,2,1]);
test.same(call_order, [1,3,2]);
- test.ok(!err);
+ test.ok(err === null, err + " passed instead of 'null'");
test.done();
});
};
@@ -1975,6 +2225,7 @@ exports['until'] = function (test) {
cb();
},
function (err) {
+ test.ok(err === null, err + " passed instead of 'null'");
test.same(call_order, [
['test', 0],
['iterator', 0], ['test', 1],
@@ -2003,6 +2254,7 @@ exports['doUntil'] = function (test) {
return (count == 5);
},
function (err) {
+ test.ok(err === null, err + " passed instead of 'null'");
test.same(call_order, [
['iterator', 0], ['test', 1],
['iterator', 1], ['test', 2],
@@ -2058,6 +2310,7 @@ exports['whilst'] = function (test) {
cb();
},
function (err) {
+ test.ok(err === null, err + " passed instead of 'null'");
test.same(call_order, [
['test', 0],
['iterator', 0], ['test', 1],
@@ -2087,6 +2340,7 @@ exports['doWhilst'] = function (test) {
return (count < 5);
},
function (err) {
+ test.ok(err === null, err + " passed instead of 'null'");
test.same(call_order, [
['iterator', 0], ['test', 1],
['iterator', 1], ['test', 2],
@@ -2236,6 +2490,13 @@ exports['queue default concurrency'] = function (test) {
};
};
+exports['queue zero concurrency'] = function(test){
+ test.throws(function () {
+ async.queue(function (task, callback) {}, 0);
+ });
+ test.done();
+};
+
exports['queue error propagation'] = function(test){
var results = [];
@@ -2423,12 +2684,12 @@ exports['queue bulk task'] = function (test) {
exports['queue idle'] = function(test) {
var q = async.queue(function (task, callback) {
// Queue is busy when workers are running
- test.equal(q.idle(), false)
+ test.equal(q.idle(), false);
callback();
}, 1);
// Queue is idle before anything added
- test.equal(q.idle(), true)
+ test.equal(q.idle(), true);
q.unshift(4);
q.unshift(3);
@@ -2436,14 +2697,14 @@ exports['queue idle'] = function(test) {
q.unshift(1);
// Queue is busy when tasks added
- test.equal(q.idle(), false)
+ test.equal(q.idle(), false);
q.drain = function() {
// Queue is idle after drain
test.equal(q.idle(), true);
test.done();
- }
-}
+ };
+};
exports['queue pause'] = function(test) {
var call_order = [],
@@ -2496,7 +2757,7 @@ exports['queue pause'] = function(test) {
]);
test.done();
}, 800);
-}
+};
exports['queue pause with concurrency'] = function(test) {
var call_order = [],
@@ -2533,6 +2794,10 @@ exports['queue pause with concurrency'] = function(test) {
}, resume_timeout);
setTimeout(function () {
+ test.equal(q.running(), 2);
+ }, resume_timeout + 10);
+
+ setTimeout(function () {
test.same(call_order, [
'process 1', 'timeout 100',
'process 2', 'timeout 100',
@@ -2543,7 +2808,31 @@ exports['queue pause with concurrency'] = function(test) {
]);
test.done();
}, 800);
-}
+};
+
+exports['queue start paused'] = function (test) {
+ var q = async.queue(function (task, callback) {
+ setTimeout(function () {
+ callback();
+ }, 10);
+ }, 2);
+ q.pause();
+
+ q.push([1, 2, 3]);
+
+ setTimeout(function () {
+ q.resume();
+ }, 10);
+
+ setTimeout(function () {
+ test.equal(q.running(), 2);
+ q.resume();
+ }, 15);
+
+ q.drain = function () {
+ test.done();
+ };
+};
exports['queue kill'] = function (test) {
var q = async.queue(function (task, callback) {
@@ -2554,7 +2843,7 @@ exports['queue kill'] = function (test) {
}, 1);
q.drain = function() {
test.ok(false, "Function should never be called");
- }
+ };
q.push(0);
@@ -2563,7 +2852,7 @@ exports['queue kill'] = function (test) {
setTimeout(function() {
test.equal(q.length(), 0);
test.done();
- }, 600)
+ }, 600);
};
exports['priorityQueue'] = function (test) {
@@ -2806,20 +3095,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();
@@ -2827,21 +3116,21 @@ 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++;
- }
+ };
loadCargo();
setTimeout(loadCargo, 500);
@@ -2853,7 +3142,7 @@ exports['cargo drain twice'] = function (test) {
};
exports['memoize'] = function (test) {
- test.expect(4);
+ test.expect(5);
var call_order = [];
var fn = function (arg1, arg2, callback) {
@@ -2865,6 +3154,7 @@ exports['memoize'] = function (test) {
var fn2 = async.memoize(fn);
fn2(1, 2, function (err, result) {
+ test.ok(err === null, err + " passed instead of 'null'");
test.equal(result, 3);
fn2(1, 2, function (err, result) {
test.equal(result, 3);
@@ -2940,7 +3230,7 @@ exports['unmemoize'] = function(test) {
});
});
});
-}
+};
exports['unmemoize a not memoized function'] = function(test) {
test.expect(1);
@@ -2955,7 +3245,7 @@ exports['unmemoize a not memoized function'] = function(test) {
});
test.done();
-}
+};
exports['memoize error'] = function (test) {
test.expect(1);
@@ -3024,22 +3314,22 @@ exports['falsy return values in series'] = function (test) {
async.nextTick(function() {
callback(null, false);
});
- };
+ }
function taskUndefined(callback) {
async.nextTick(function() {
callback(null, undefined);
});
- };
+ }
function taskEmpty(callback) {
async.nextTick(function() {
callback(null);
});
- };
+ }
function taskNull(callback) {
async.nextTick(function() {
callback(null, null);
});
- };
+ }
async.series(
[taskFalse, taskUndefined, taskEmpty, taskNull],
function(err, results) {
@@ -3059,22 +3349,22 @@ exports['falsy return values in parallel'] = function (test) {
async.nextTick(function() {
callback(null, false);
});
- };
+ }
function taskUndefined(callback) {
async.nextTick(function() {
callback(null, undefined);
});
- };
+ }
function taskEmpty(callback) {
async.nextTick(function() {
callback(null);
});
- };
+ }
function taskNull(callback) {
async.nextTick(function() {
callback(null, null);
});
- };
+ }
async.parallel(
[taskFalse, taskUndefined, taskEmpty, taskNull],
function(err, results) {
@@ -3102,12 +3392,12 @@ exports['queue events'] = function(test) {
calls.push('saturated');
};
q.empty = function() {
- test.ok(q.length() == 0, 'queue should be empty now');
+ test.ok(q.length() === 0, 'queue should be empty now');
calls.push('empty');
};
q.drain = function() {
test.ok(
- q.length() == 0 && q.running() == 0,
+ q.length() === 0 && q.running() === 0,
'queue should be empty now and no more workers should be running'
);
calls.push('drain');
@@ -3145,7 +3435,7 @@ exports['queue empty'] = function(test) {
q.drain = function() {
test.ok(
- q.length() == 0 && q.running() == 0,
+ q.length() === 0 && q.running() === 0,
'queue should be empty now and no more workers should be running'
);
calls.push('drain');
@@ -3157,11 +3447,30 @@ exports['queue empty'] = function(test) {
q.push([]);
};
+exports['queue saturated'] = function (test) {
+ var saturatedCalled = false;
+ var q = async.queue(function(task, cb) {
+ async.setImmediate(cb);
+ }, 2);
+
+ q.saturated = function () {
+ saturatedCalled = true;
+ };
+ q.drain = function () {
+ test.ok(saturatedCalled, "saturated not called");
+ test.done();
+ };
+
+ setTimeout(function () {
+ q.push(['foo', 'bar', 'baz', 'moo']);
+ }, 10);
+};
+
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);