summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteve Robb <softnfuzzyrobb@gmail.com>2016-01-06 23:32:08 +0000
committerSteve Robb <softnfuzzyrobb@gmail.com>2016-01-06 23:32:08 +0000
commita6d5e9503752f78a85bb03665c4d1fe4d0e12690 (patch)
tree63b21702f84f51cf5117c6abde1f34857cfea75f
parente56a37170b017c3bade81eedab57a99a5e2f019f (diff)
parent11f5d1f4ac46932a007b87757af3fb01db38d03e (diff)
downloadasync-a6d5e9503752f78a85bb03665c4d1fe4d0e12690.tar.gz
Merge branch 'master' into autoInjectPR
Conflicts: lib/async.js test/test-async.js
-rw-r--r--.gitignore8
-rw-r--r--.jscsrc3
-rw-r--r--.jshintrc29
-rw-r--r--.npmignore7
-rw-r--r--.travis.yml11
-rw-r--r--CHANGELOG.md120
-rw-r--r--Makefile32
-rw-r--r--README.md745
-rw-r--r--bower.json57
-rw-r--r--component.json18
-rw-r--r--deps/nodeunit.js305
-rw-r--r--dist/async.js1264
-rw-r--r--dist/async.min.js2
-rw-r--r--dist/async.min.map1
-rw-r--r--karma.conf.js12
-rwxr-xr-xlib/async.js1546
-rw-r--r--mocha_test/.jshintrc4
-rw-r--r--mocha_test/compose.js86
-rw-r--r--mocha_test/forever.js44
-rw-r--r--mocha_test/support/is_browser.js4
-rw-r--r--nodelint.cfg4
-rw-r--r--package.json115
-rwxr-xr-xperf/benchmark.js228
-rw-r--r--perf/memory.js46
-rw-r--r--perf/suites.js297
-rwxr-xr-xsupport/sync-package-managers.js53
-rwxr-xr-xtest/test-async.js2376
27 files changed, 5745 insertions, 1672 deletions
diff --git a/.gitignore b/.gitignore
index 8225baa..9134974 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,6 @@
-/node_modules
-/dist
+node_modules
+dist
+perf/versions
+nyc_output
+coverage
+*.log
diff --git a/.jscsrc b/.jscsrc
new file mode 100644
index 0000000..b8cfa17
--- /dev/null
+++ b/.jscsrc
@@ -0,0 +1,3 @@
+{
+ "validateIndentation": 4
+} \ No newline at end of file
diff --git a/.jshintrc b/.jshintrc
new file mode 100644
index 0000000..76be34a
--- /dev/null
+++ b/.jshintrc
@@ -0,0 +1,29 @@
+{
+ // Enforcing options
+ "eqeqeq": false,
+ "forin": true,
+ "indent": 4,
+ "noarg": true,
+ "undef": true,
+ "unused": 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": {
+ "self": true,
+ "define": true,
+ "describe": true,
+ "context": true,
+ "it": true
+ }
+}
diff --git a/.npmignore b/.npmignore
deleted file mode 100644
index 392d66e..0000000
--- a/.npmignore
+++ /dev/null
@@ -1,7 +0,0 @@
-deps
-dist
-test
-nodelint.cfg
-.npmignore
-.gitmodules
-Makefile
diff --git a/.travis.yml b/.travis.yml
index 6e5919d..a866e55 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,3 +1,14 @@
language: node_js
node_js:
- "0.10"
+ - "0.12"
+ - "4"
+ - "5"
+sudo: false
+after_success: npm run coveralls
+
+# Needed to run Karma with Firefox on Travis
+# http://karma-runner.github.io/0.13/plus/travis.html
+before_script:
+ - export DISPLAY=:99.0
+ - sh -e /etc/init.d/xvfb start
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..fa32563
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,120 @@
+# v1.5.1
+- Fix issue with `pause` in `queue` with concurrency enabled (#946)
+- `while` and `until` now pass the final result to callback (#963)
+- `auto` will properly handle concurrency when there is no callback (#966)
+- `auto` will now properly stop execution when an error occurs (#988, #993)
+- Various doc fixes (#971, #980)
+
+# v1.5.0
+
+- Added `transform`, analogous to [`_.transform`](http://lodash.com/docs#transform) (#892)
+- `map` now returns an object when an object is passed in, rather than array with non-numeric keys. `map` will begin always returning an array with numeric indexes in the next major release. (#873)
+- `auto` now accepts an optional `concurrency` argument to limit the number of running tasks (#637)
+- Added `queue#workersList()`, to retrieve the list of currently running tasks. (#891)
+- Various code simplifications (#896, #904)
+- Various doc fixes :scroll: (#890, #894, #903, #905, #912)
+
+# v1.4.2
+
+- Ensure coverage files don't get published on npm (#879)
+
+# v1.4.1
+
+- Add in overlooked `detectLimit` method (#866)
+- Removed unnecessary files from npm releases (#861)
+- Removed usage of a reserved word to prevent :boom: in older environments (#870)
+
+# v1.4.0
+
+- `asyncify` now supports promises (#840)
+- Added `Limit` versions of `filter` and `reject` (#836)
+- Add `Limit` versions of `detect`, `some` and `every` (#828, #829)
+- `some`, `every` and `detect` now short circuit early (#828, #829)
+- Improve detection of the global object (#804), enabling use in WebWorkers
+- `whilst` now called with arguments from iterator (#823)
+- `during` now gets called with arguments from iterator (#824)
+- Code simplifications and optimizations aplenty ([diff](https://github.com/caolan/async/compare/v1.3.0...v1.4.0))
+
+
+# v1.3.0
+
+New Features:
+- Added `constant`
+- Added `asyncify`/`wrapSync` for making sync functions work with callbacks. (#671, #806)
+- Added `during` and `doDuring`, which are like `whilst` with an async truth test. (#800)
+- `retry` now accepts an `interval` parameter to specify a delay between retries. (#793)
+- `async` should work better in Web Workers due to better `root` detection (#804)
+- Callbacks are now optional in `whilst`, `doWhilst`, `until`, and `doUntil` (#642)
+- Various internal updates (#786, #801, #802, #803)
+- Various doc fixes (#790, #794)
+
+Bug Fixes:
+- `cargo` now exposes the `payload` size, and `cargo.payload` can be changed on the fly after the `cargo` is created. (#740, #744, #783)
+
+
+# v1.2.1
+
+Bug Fix:
+
+- Small regression with synchronous iterator behavior in `eachSeries` with a 1-element array. Before 1.1.0, `eachSeries`'s callback was called on the same tick, which this patch restores. In 2.0.0, it will be called on the next tick. (#782)
+
+
+# v1.2.0
+
+New Features:
+
+- Added `timesLimit` (#743)
+- `concurrency` can be changed after initialization in `queue` by setting `q.concurrency`. The new concurrency will be reflected the next time a task is processed. (#747, #772)
+
+Bug Fixes:
+
+- Fixed a regression in `each` and family with empty arrays that have additional properties. (#775, #777)
+
+
+# v1.1.1
+
+Bug Fix:
+
+- Small regression with synchronous iterator behavior in `eachSeries` with a 1-element array. Before 1.1.0, `eachSeries`'s callback was called on the same tick, which this patch restores. In 2.0.0, it will be called on the next tick. (#782)
+
+
+# v1.1.0
+
+New Features:
+
+- `cargo` now supports all of the same methods and event callbacks as `queue`.
+- Added `ensureAsync` - A wrapper that ensures an async function calls its callback on a later tick. (#769)
+- Optimized `map`, `eachOf`, and `waterfall` families of functions
+- Passing a `null` or `undefined` array to `map`, `each`, `parallel` and families will be treated as an empty array (#667).
+- The callback is now optional for the composed results of `compose` and `seq`. (#618)
+- Reduced file size by 4kb, (minified version by 1kb)
+- Added code coverage through `nyc` and `coveralls` (#768)
+
+Bug Fixes:
+
+- `forever` will no longer stack overflow with a synchronous iterator (#622)
+- `eachLimit` and other limit functions will stop iterating once an error occurs (#754)
+- Always pass `null` in callbacks when there is no error (#439)
+- Ensure proper conditions when calling `drain()` after pushing an empty data set to a queue (#668)
+- `each` and family will properly handle an empty array (#578)
+- `eachSeries` and family will finish if the underlying array is modified during execution (#557)
+- `queue` will throw if a non-function is passed to `q.push()` (#593)
+- Doc fixes (#629, #766)
+
+
+# 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..87279f4 100644
--- a/Makefile
+++ b/Makefile
@@ -1,25 +1,37 @@
+export PATH := ./node_modules/.bin/:$(PATH):./bin/
+
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
+XYZ = node_modules/.bin/xyz --repo git@github.com:caolan/async.git
BUILDDIR = dist
+SRC = lib/async.js
-all: clean test build
+all: lint test clean build
build: $(wildcard lib/*.js)
mkdir -p $(BUILDDIR)
- $(UGLIFY) lib/async.js > $(BUILDDIR)/async.min.js
+ cp $(SRC) $(BUILDDIR)/async.js
+ uglifyjs $(BUILDDIR)/async.js -mc \
+ --source-map $(BUILDDIR)/async.min.map \
+ -o $(BUILDDIR)/async.min.js
test:
- $(NODEUNIT) test
+ nodeunit test
clean:
rm -rf $(BUILDDIR)
lint:
- $(NODELINT) --config nodelint.cfg lib/async.js
+ jshint $(SRC) test/*.js mocha_test/* perf/*.js
+ jscs $(SRC) test/*.js mocha_test/* perf/*.js
+
+.PHONY: test lint build all clean
+
-.PHONY: test build all
+.PHONY: release-major release-minor release-patch
+release-major release-minor release-patch: all
+ ./support/sync-package-managers.js
+ git add --force *.json
+ git add --force $(BUILDDIR)
+ git commit -am "update minified build"; true
+ $(XYZ) --increment $(@:release-%=%)
diff --git a/README.md b/README.md
index 60ed04a..0d75594 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,23 @@
# 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)
+[![Coverage Status](https://coveralls.io/repos/caolan/async/badge.svg?branch=master)](https://coveralls.io/r/caolan/async?branch=master)
+[![Join the chat at https://gitter.im/caolan/async](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/caolan/async?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
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
@@ -41,7 +52,66 @@ There are many more functions available so take a look at the docs below for a
full list. This module aims to be comprehensive, so if you feel anything is
missing please create a GitHub issue for it.
-## Common Pitfalls
+## Common Pitfalls <sub>[(StackOverflow)](http://stackoverflow.com/questions/tagged/async.js)</sub>
+### Synchronous iteration functions
+
+If you get an error like `RangeError: Maximum call stack size exceeded.` or other stack overflow issues when using async, you are likely using a synchronous iterator. By *synchronous* we mean a function that calls its callback on the same tick in the javascript event loop, without doing any I/O or using any timers. Calling many callbacks iteratively will quickly overflow the stack. If you run into this issue, just defer your callback with `async.setImmediate` to start a new call stack on the next tick of the event loop.
+
+This can also arise by accident if you callback early in certain cases:
+
+```js
+async.eachSeries(hugeArray, function iterator(item, callback) {
+ if (inCache(item)) {
+ callback(null, cache[item]); // if many items are cached, you'll overflow
+ } else {
+ doSomeIO(item, callback);
+ }
+}, function done() {
+ //...
+});
+```
+
+Just change it to:
+
+```js
+async.eachSeries(hugeArray, function iterator(item, callback) {
+ if (inCache(item)) {
+ async.setImmediate(function () {
+ callback(null, cache[item]);
+ });
+ } else {
+ doSomeIO(item, callback);
+ //...
+```
+
+Async guards against synchronous functions in some, but not all, cases. If you are still running into stack overflows, you can defer as suggested above, or wrap functions with [`async.ensureAsync`](#ensureAsync) Functions that are asynchronous by their nature do not have this problem and don't need the extra callback deferral.
+
+If JavaScript's event loop is still a bit nebulous, check out [this article](http://blog.carbonfive.com/2013/10/27/the-javascript-event-loop-explained/) or [this talk](http://2014.jsconf.eu/speakers/philip-roberts-what-the-heck-is-the-event-loop-anyway.html) for more detailed information about how it works.
+
+
+### Multiple callbacks
+
+Make sure to always `return` when calling a callback early, otherwise you will cause multiple callbacks and unpredictable behavior in many cases.
+
+```js
+async.waterfall([
+ function (callback) {
+ getSomething(options, function (err, result) {
+ if (err) {
+ callback(new Error("failed getting something:" + err.message));
+ // we should return here
+ }
+ // since we did not return, this callback still will be called and
+ // `processData` will be called twice
+ callback(null, result);
+ });
+ },
+ processData
+], done)
+```
+
+It is always good practice to `return callback(err, result)` whenever a callback call is not the last statement of a function.
+
### Binding a context to an iterator
@@ -53,7 +123,7 @@ a method of another library isn't working as an iterator, study this example:
// Here is a simple object with an (unnecessarily roundabout) squaring method
var AsyncSquaringLibrary = {
squareExponent: 2,
- square: function(number, callback){
+ square: function(number, callback){
var result = Math.pow(number, this.squareExponent);
setTimeout(function(){
callback(null, result);
@@ -71,7 +141,7 @@ async.map([1, 2, 3], AsyncSquaringLibrary.square, function(err, result){
async.map([1, 2, 3], AsyncSquaringLibrary.square.bind(AsyncSquaringLibrary), function(err, result){
// result is [1, 4, 9]
// With the help of bind we can attach a context to the iterator before
- // passing it to async. Now the square function will be executed in its
+ // passing it to async. Now the square function will be executed in its
// 'home' AsyncSquaringLibrary context and the value of `this.squareExponent`
// will be as expected.
});
@@ -80,16 +150,20 @@ 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
-So far it's been tested in IE6, IE7, IE8, FF3.6 and Chrome 5.
+So far it's been tested in IE6, IE7, IE8, FF3.6 and Chrome 5.
Usage:
@@ -106,69 +180,63 @@ Usage:
## Documentation
+Some functions are also available in the following forms:
+* `<name>Series` - the same as `<name>` but runs only a single async operation at a time
+* `<name>Limit` - the same as `<name>` but runs a maximum of `limit` async operations at a time
+
### Collections
-* [`each`](#each)
-* [`eachSeries`](#eachSeries)
-* [`eachLimit`](#eachLimit)
-* [`map`](#map)
-* [`mapSeries`](#mapSeries)
-* [`mapLimit`](#mapLimit)
-* [`filter`](#filter)
-* [`filterSeries`](#filterSeries)
-* [`reject`](#reject)
-* [`rejectSeries`](#rejectSeries)
-* [`reduce`](#reduce)
-* [`reduceRight`](#reduceRight)
-* [`detect`](#detect)
-* [`detectSeries`](#detectSeries)
+* [`each`](#each), `eachSeries`, `eachLimit`
+* [`forEachOf`](#forEachOf), `forEachOfSeries`, `forEachOfLimit`
+* [`map`](#map), `mapSeries`, `mapLimit`
+* [`filter`](#filter), `filterSeries`, `filterLimit`
+* [`reject`](#reject), `rejectSeries`, `rejectLimit`
+* [`reduce`](#reduce), [`reduceRight`](#reduceRight)
+* [`detect`](#detect), `detectSeries`, `detectLimit`
* [`sortBy`](#sortBy)
-* [`some`](#some)
-* [`every`](#every)
-* [`concat`](#concat)
-* [`concatSeries`](#concatSeries)
+* [`some`](#some), `someLimit`
+* [`every`](#every), `everyLimit`
+* [`concat`](#concat), `concatSeries`
### Control Flow
* [`series`](#seriestasks-callback)
-* [`parallel`](#parallel)
-* [`parallelLimit`](#parallellimittasks-limit-callback)
-* [`whilst`](#whilst)
-* [`doWhilst`](#doWhilst)
-* [`until`](#until)
-* [`doUntil`](#doUntil)
+* [`parallel`](#parallel), `parallelLimit`
+* [`whilst`](#whilst), [`doWhilst`](#doWhilst)
+* [`until`](#until), [`doUntil`](#doUntil)
+* [`during`](#during), [`doDuring`](#doDuring)
* [`forever`](#forever)
* [`waterfall`](#waterfall)
* [`compose`](#compose)
* [`seq`](#seq)
-* [`applyEach`](#applyEach)
-* [`applyEachSeries`](#applyEachSeries)
-* [`queue`](#queue)
-* [`priorityQueue`](#priorityQueue)
+* [`applyEach`](#applyEach), `applyEachSeries`
+* [`queue`](#queue), [`priorityQueue`](#priorityQueue)
* [`cargo`](#cargo)
* [`auto`](#auto)
* [`autoInject`](#autoInject)
* [`retry`](#retry)
* [`iterator`](#iterator)
-* [`apply`](#apply)
-* [`nextTick`](#nextTick)
-* [`times`](#times)
-* [`timesSeries`](#timesSeries)
+* [`times`](#times), `timesSeries`, `timesLimit`
### Utils
+* [`apply`](#apply)
+* [`nextTick`](#nextTick)
* [`memoize`](#memoize)
* [`unmemoize`](#unmemoize)
+* [`ensureAsync`](#ensureAsync)
+* [`constant`](#constant)
+* [`asyncify`](#asyncify)
+* [`wrapSync`](#wrapSync)
* [`log`](#log)
* [`dir`](#dir)
* [`noConflict`](#noConflict)
-
## Collections
<a name="forEach" />
<a name="each" />
-### each(arr, iterator, callback)
+### each(arr, iterator, [callback])
Applies the function `iterator` to each item in `arr`, in parallel.
The `iterator` is called with an item from the list, and a callback for when it
@@ -182,10 +250,11 @@ __Arguments__
* `arr` - An array to iterate over.
* `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.
-* `callback(err)` - A callback which is called when all `iterator` functions
+ 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. The array index is not passed
+ to the iterator. If you need the index, use [`forEachOf`](#forEachOf).
+* `callback(err)` - *Optional* A callback which is called when all `iterator` functions
have finished, or an error occurs.
__Examples__
@@ -201,13 +270,13 @@ async.each(openFiles, saveFile, function(err){
```
```js
-// assuming openFiles is an array of file names
+// 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);
-
+
if( file.length > 32 ) {
console.log('This file name is too long');
callback('File name too long');
@@ -228,73 +297,80 @@ async.each(openFiles, function( file, callback) {
});
```
----------------------------------------
-
-<a name="forEachSeries" />
-<a name="eachSeries" />
-### eachSeries(arr, iterator, callback)
-
-The same as [`each`](#each), only `iterator` is applied to each item in `arr` in
-series. The next `iterator` is only called once the current one has completed.
-This means the `iterator` functions will complete in order.
+__Related__
+* eachSeries(arr, iterator, [callback])
+* eachLimit(arr, limit, iterator, [callback])
---------------------------------------
-<a name="forEachLimit" />
-<a name="eachLimit" />
-### eachLimit(arr, limit, iterator, callback)
+<a name="forEachOf" />
+<a name="eachOf" />
-The same as [`each`](#each), only no more than `limit` `iterator`s will be simultaneously
-running at any time.
+### forEachOf(obj, iterator, [callback])
-Note that the items in `arr` are not processed in batches, so there is no guarantee that
-the first `limit` `iterator` functions will complete before any others are started.
+Like `each`, except that it iterates over objects, and passes the key as the second argument to the iterator.
__Arguments__
-* `arr` - An array to iterate over.
-* `limit` - The maximum number of `iterator`s to run at any time.
-* `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.
-* `callback(err)` - A callback which is called when all `iterator` functions
- have finished, or an error occurs.
+* `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)` - *Optional* A callback which is called when all `iterator` functions have finished, or an error occurs.
__Example__
```js
-// Assume documents is an array of JSON objects and requestApi is a
-// function that interacts with a rate-limited REST api.
-
-async.eachLimit(documents, 20, requestApi, function(err){
- // if any of the saves produced an error, err would equal that error
-});
+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);
+})
```
+__Related__
+
+* forEachOfSeries(obj, iterator, [callback])
+* forEachOfLimit(obj, limit, iterator, [callback])
+
---------------------------------------
<a name="map" />
-### map(arr, iterator, callback)
+### 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
+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 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,
-there is no guarantee that the `iterator` functions will complete in order.
+there is no guarantee that the `iterator` functions will complete in order.
However, the results array will be in the same order as the original `arr`.
__Arguments__
* `arr` - An array to iterate over.
* `iterator(item, callback)` - A function to apply to each item in `arr`.
- The iterator is passed a `callback(err, transformed)` which must be called once
+ The iterator is passed a `callback(err, transformed)` which must be called once
it has completed with an error (which can be `null`) and a transformed item.
-* `callback(err, results)` - A callback which is called when all `iterator`
+* `callback(err, results)` - *Optional* A callback which is called when all `iterator`
functions have finished, or an error occurs. Results is an array of the
transformed items from the `arr`.
@@ -306,51 +382,15 @@ async.map(['file1','file2','file3'], fs.stat, function(err, results){
});
```
----------------------------------------
-
-<a name="mapSeries" />
-### mapSeries(arr, iterator, callback)
-
-The same as [`map`](#map), only the `iterator` is applied to each item in `arr` in
-series. The next `iterator` is only called once the current one has completed.
-The results array will be in the same order as the original.
-
-
----------------------------------------
-
-<a name="mapLimit" />
-### mapLimit(arr, limit, iterator, callback)
-
-The same as [`map`](#map), only no more than `limit` `iterator`s will be simultaneously
-running at any time.
-
-Note that the items are not processed in batches, so there is no guarantee that
-the first `limit` `iterator` functions will complete before any others are started.
-
-__Arguments__
-
-* `arr` - An array to iterate over.
-* `limit` - The maximum number of `iterator`s to run at any time.
-* `iterator(item, callback)` - A function to apply to each item in `arr`.
- The iterator is passed a `callback(err, transformed)` which must be called once
- it has completed with an error (which can be `null`) and a transformed item.
-* `callback(err, results)` - A callback which is called when all `iterator`
- calls have finished, or an error occurs. The result is an array of the
- transformed items from the original `arr`.
-
-__Example__
-
-```js
-async.mapLimit(['file1','file2','file3'], 1, fs.stat, function(err, results){
- // results is now an array of stats for each file
-});
-```
+__Related__
+* mapSeries(arr, iterator, [callback])
+* mapLimit(arr, limit, iterator, [callback])
---------------------------------------
<a name="select" />
<a name="filter" />
-### filter(arr, iterator, callback)
+### filter(arr, iterator, [callback])
__Alias:__ `select`
@@ -365,9 +405,9 @@ __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
+ The `iterator` is passed a `callback(truthValue)`, which must be called with a
boolean argument once it has completed.
-* `callback(results)` - A callback which is called after all the `iterator`
+* `callback(results)` - *Optional* A callback which is called after all the `iterator`
functions have finished.
__Example__
@@ -378,48 +418,37 @@ async.filter(['file1','file2','file3'], fs.exists, function(results){
});
```
----------------------------------------
-
-<a name="selectSeries" />
-<a name="filterSeries" />
-### filterSeries(arr, iterator, callback)
+__Related__
-__Alias:__ `selectSeries`
-
-The same as [`filter`](#filter) only the `iterator` is applied to each item in `arr` in
-series. The next `iterator` is only called once the current one has completed.
-The results array will be in the same order as the original.
+* filterSeries(arr, iterator, [callback])
+* filterLimit(arr, limit, iterator, [callback])
---------------------------------------
<a name="reject" />
-### reject(arr, iterator, callback)
+### reject(arr, iterator, [callback])
The opposite of [`filter`](#filter). Removes values that pass an `async` truth test.
----------------------------------------
-
-<a name="rejectSeries" />
-### rejectSeries(arr, iterator, callback)
-
-The same as [`reject`](#reject), only the `iterator` is applied to each item in `arr`
-in series.
+__Related__
+* rejectSeries(arr, iterator, [callback])
+* rejectLimit(arr, limit, iterator, [callback])
---------------------------------------
<a name="reduce" />
-### reduce(arr, memo, iterator, callback)
+### reduce(arr, memo, iterator, [callback])
__Aliases:__ `inject`, `foldl`
Reduces `arr` into a single value using an async `iterator` to return
-each successive step. `memo` is the initial state of the reduction.
-This function only operates in series.
+each successive step. `memo` is the initial state of the reduction.
+This function only operates in series.
-For performance reasons, it may make sense to split a call to this function into
-a parallel map, and then use the normal `Array.prototype.reduce` on the results.
-This function is for situations where each step in the reduction needs to be async;
+For performance reasons, it may make sense to split a call to this function into
+a parallel map, and then use the normal `Array.prototype.reduce` on the results.
+This function is for situations where each step in the reduction needs to be async;
if you can get the data before reducing it, then it's probably a good idea to do so.
__Arguments__
@@ -428,11 +457,11 @@ __Arguments__
* `memo` - The initial state of the reduction.
* `iterator(memo, item, callback)` - A function applied to each item in the
array to produce the next step in the reduction. The `iterator` is passed a
- `callback(err, reduction)` which accepts an optional error as its first
- argument, and the state of the reduction as the second. If an error is
- passed to the callback, the reduction is stopped and the main `callback` is
+ `callback(err, reduction)` which accepts an optional error as its first
+ argument, and the state of the reduction as the second. If an error is
+ passed to the callback, the reduction is stopped and the main `callback` is
immediately called with the error.
-* `callback(err, result)` - A callback which is called after all the `iterator`
+* `callback(err, result)` - *Optional* A callback which is called after all the `iterator`
functions have finished. Result is the reduced value.
__Example__
@@ -451,7 +480,7 @@ async.reduce([1,2,3], 0, function(memo, item, callback){
---------------------------------------
<a name="reduceRight" />
-### reduceRight(arr, memo, iterator, callback)
+### reduceRight(arr, memo, iterator, [callback])
__Alias:__ `foldr`
@@ -461,7 +490,7 @@ Same as [`reduce`](#reduce), only operates on `arr` in reverse order.
---------------------------------------
<a name="detect" />
-### detect(arr, iterator, callback)
+### detect(arr, iterator, [callback])
Returns the first value in `arr` that passes an async truth test. The
`iterator` is applied in parallel, meaning the first iterator to return `true` will
@@ -474,12 +503,12 @@ __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.
-* `callback(result)` - A callback which is called as soon as any iterator returns
+ The iterator is passed a `callback(truthValue)` which must be called with a
+ boolean argument once it has completed. **Note: this callback does not take an error as its first argument.**
+* `callback(result)` - *Optional* A callback which is called as soon as any 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__
@@ -489,20 +518,15 @@ async.detect(['file1','file2','file3'], fs.exists, function(result){
});
```
----------------------------------------
-
-<a name="detectSeries" />
-### detectSeries(arr, iterator, callback)
-
-The same as [`detect`](#detect), only the `iterator` is applied to each item in `arr`
-in series. This means the result is always the first in the original `arr` (in
-terms of array order) that passes the truth test.
+__Related__
+* detectSeries(arr, iterator, [callback])
+* detectLimit(arr, limit, iterator, [callback])
---------------------------------------
<a name="sortBy" />
-### sortBy(arr, iterator, callback)
+### sortBy(arr, iterator, [callback])
Sorts a list by the results of running each `arr` value through an async `iterator`.
@@ -513,7 +537,7 @@ __Arguments__
The iterator is passed a `callback(err, sortValue)` which must be called once it
has completed with an error (which can be `null`) and a value to use as the sort
criteria.
-* `callback(err, results)` - A callback which is called after all the `iterator`
+* `callback(err, results)` - *Optional* A callback which is called after all the `iterator`
functions have finished, or an error occurs. Results is the items from
the original `arr` sorted by the values returned by the `iterator` calls.
@@ -537,14 +561,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
} );
@@ -553,7 +577,7 @@ async.sortBy([1,9,3,5], function(x, callback){
---------------------------------------
<a name="some" />
-### some(arr, iterator, callback)
+### some(arr, iterator, [callback])
__Alias:__ `any`
@@ -567,12 +591,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
+* `callback(result)` - *Optional* 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
@@ -581,10 +606,14 @@ async.some(['file1','file2','file3'], fs.exists, function(result){
});
```
+__Related__
+
+* someLimit(arr, limit, iterator, callback)
+
---------------------------------------
<a name="every" />
-### every(arr, iterator, callback)
+### every(arr, iterator, [callback])
__Alias:__ `all`
@@ -597,11 +626,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 after all the `iterator`
- functions have finished. Result will be either `true` or `false` depending on
- the values of the async tests.
+* `callback(result)` - *Optional* A callback which is called as soon as any iterator returns
+ `false`, 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__
@@ -611,10 +642,14 @@ async.every(['file1','file2','file3'], fs.exists, function(result){
});
```
+__Related__
+
+* everyLimit(arr, limit, iterator, callback)
+
---------------------------------------
<a name="concat" />
-### concat(arr, iterator, callback)
+### concat(arr, iterator, [callback])
Applies `iterator` to each item in `arr`, concatenating the results. Returns the
concatenated list. The `iterator`s are called in parallel, and the results are
@@ -625,9 +660,9 @@ __Arguments__
* `arr` - An array to iterate over.
* `iterator(item, callback)` - A function to apply to each item in `arr`.
- The iterator is passed a `callback(err, results)` which must be called once it
+ The iterator is passed a `callback(err, results)` which must be called once it
has completed with an error (which can be `null`) and an array of results.
-* `callback(err, results)` - A callback which is called after all the `iterator`
+* `callback(err, results)` - *Optional* A callback which is called after all the `iterator`
functions have finished, or an error occurs. Results is an array containing
the concatenated results of the `iterator` function.
@@ -639,12 +674,9 @@ async.concat(['dir1','dir2','dir3'], fs.readdir, function(err, files){
});
```
----------------------------------------
-
-<a name="concatSeries" />
-### concatSeries(arr, iterator, callback)
+__Related__
-Same as [`concat`](#concat), but executes in series instead of parallel.
+* concatSeries(arr, iterator, [callback])
## Control Flow
@@ -654,7 +686,7 @@ Same as [`concat`](#concat), but executes in series instead of parallel.
Run the functions in the `tasks` array in series, each one running once the previous
function has completed. If any functions in the series pass an error to its
-callback, no more functions are run, and `callback` is immediately called with the value of the error.
+callback, no more functions are run, and `callback` is immediately called with the value of the error.
Otherwise, `callback` receives an array of results when `tasks` have completed.
It is also possible to use an object instead of an array. Each property will be
@@ -663,13 +695,13 @@ instead of an array. This can be a more readable way of handling results from
[`series`](#series).
**Note** that while many implementations preserve the order of object properties, the
-[ECMAScript Language Specifcation](http://www.ecma-international.org/ecma-262/5.1/#sec-8.6)
+[ECMAScript Language Specification](http://www.ecma-international.org/ecma-262/5.1/#sec-8.6)
explicitly states that
> The mechanics and order of enumerating the properties is not specified.
So if you rely on the order in which your series of functions are executed, and want
-this to work on all platforms, consider using an array.
+this to work on all platforms, consider using an array.
__Arguments__
@@ -677,7 +709,7 @@ __Arguments__
a `callback(err, result)` it must call on completion with an error `err` (which can
be `null`) and an optional `result` value.
* `callback(err, results)` - An optional callback to run once all the functions
- have completed. This function gets a results array (or object) containing all
+ have completed. This function gets a results array (or object) containing all
the result arguments passed to the `task` callbacks.
__Example__
@@ -728,6 +760,8 @@ callback, the main `callback` is immediately called with the value of the error.
Once the `tasks` have completed, the results are passed to the final `callback` as an
array.
+**Note:** `parallel` is about kicking-off I/O tasks in parallel, not about parallel execution of code. If your tasks do not use any timers or perform any I/O, they will actually be executed in series. Any synchronous setup sections for each task will happen one after the other. JavaScript remains single-threaded.
+
It is also possible to use an object instead of an array. Each property will be
run as a function and the results will be passed to the final `callback` as an object
instead of an array. This can be a more readable way of handling results from
@@ -736,11 +770,11 @@ instead of an array. This can be a more readable way of handling results from
__Arguments__
-* `tasks` - An array or object containing functions to run. Each function is passed
- a `callback(err, result)` which it must call on completion with an error `err`
+* `tasks` - An array or object containing functions to run. Each function is passed
+ a `callback(err, result)` which it must call on completion with an error `err`
(which can be `null`) and an optional `result` value.
* `callback(err, results)` - An optional callback to run once all the functions
- have completed. This function gets a results array (or object) containing all
+ have completed successfully. This function gets a results array (or object) containing all
the result arguments passed to the task callbacks.
__Example__
@@ -783,26 +817,9 @@ function(err, results) {
});
```
----------------------------------------
-
-<a name="parallelLimit" />
-### parallelLimit(tasks, limit, [callback])
-
-The same as [`parallel`](#parallel), only `tasks` are executed in parallel
-with a maximum of `limit` tasks executing at any time.
+__Related__
-Note that the `tasks` are not executed in batches, so there is no guarantee that
-the first `limit` tasks will complete before any others are started.
-
-__Arguments__
-
-* `tasks` - An array or object containing functions to run, each function is passed
- a `callback(err, result)` it must call on completion with an error `err` (which can
- be `null`) and an optional `result` value.
-* `limit` - The maximum number of `tasks` to run at any time.
-* `callback(err, results)` - An optional callback to run once all the functions
- have completed. This function gets a results array (or object) containing all
- the result arguments passed to the `task` callbacks.
+* parallelLimit(tasks, limit, [callback])
---------------------------------------
@@ -816,10 +833,11 @@ __Arguments__
* `test()` - synchronous truth test to perform before each execution of `fn`.
* `fn(callback)` - A function which is called each time `test` passes. The function is
- passed a `callback(err)`, which must be called once it has completed with an
+ passed a `callback(err)`, which must be called once it has completed with an
optional `err` argument.
-* `callback(err)` - A callback which is called after the test fails and repeated
- execution of `fn` has stopped.
+* `callback(err, [results])` - A callback which is called after the test
+ function has failed and repeated execution of `fn` has stopped. `callback`
+ will be passed an error and any arguments passed to the final `fn`'s callback.
__Example__
@@ -830,10 +848,12 @@ async.whilst(
function () { return count < 5; },
function (callback) {
count++;
- setTimeout(callback, 1000);
+ setTimeout(function () {
+ callback(null, count);
+ }, 1000);
},
- function (err) {
- // 5 seconds have passed
+ function (err, n) {
+ // 5 seconds have passed, n = 5
}
);
```
@@ -843,8 +863,8 @@ async.whilst(
<a name="doWhilst" />
### doWhilst(fn, test, callback)
-The post-check version of [`whilst`](#whilst). To reflect the difference in
-the order of operations, the arguments `test` and `fn` are switched.
+The post-check version of [`whilst`](#whilst). To reflect the difference in
+the order of operations, the arguments `test` and `fn` are switched.
`doWhilst` is to `whilst` as `do while` is to `while` in plain JavaScript.
@@ -854,7 +874,8 @@ the order of operations, the arguments `test` and `fn` are switched.
### until(test, fn, callback)
Repeatedly call `fn` until `test` returns `true`. Calls `callback` when stopped,
-or an error occurs.
+or an error occurs. `callback` will be passed an error and any arguments passed
+to the final `fn`'s callback.
The inverse of [`whilst`](#whilst).
@@ -867,8 +888,44 @@ Like [`doWhilst`](#doWhilst), except the `test` is inverted. Note the argument o
---------------------------------------
+<a name="during" />
+### during(test, fn, callback)
+
+Like [`whilst`](#whilst), except the `test` is an asynchronous function that is passed a callback in the form of `function (err, truth)`. If error is passed to `test` or `fn`, the main callback is immediately called with the value of the error.
+
+__Example__
+
+```js
+var count = 0;
+
+async.during(
+ function (callback) {
+ return callback(null, count < 5);
+ },
+ function (callback) {
+ count++;
+ setTimeout(callback, 1000);
+ },
+ function (err) {
+ // 5 seconds have passed
+ }
+);
+```
+
+---------------------------------------
+
+<a name="doDuring" />
+### doDuring(fn, test, callback)
+
+The post-check version of [`during`](#during). To reflect the difference in
+the order of operations, the arguments `test` and `fn` are switched.
+
+Also a version of [`doWhilst`](#doWhilst) with asynchronous `test` function.
+
+---------------------------------------
+
<a name="forever" />
-### forever(fn, errback)
+### forever(fn, [errback])
Calls the asynchronous function `fn` with a callback parameter that allows it to
call itself again, in series, indefinitely.
@@ -901,9 +958,9 @@ the error.
__Arguments__
-* `tasks` - An array of functions to run, each function is passed a
+* `tasks` - An array of functions to run, each function is passed a
`callback(err, result1, result2, ...)` it must call on completion. The first
- argument is an error (which can be `null`) and any further arguments will be
+ argument is an error (which can be `null`) and any further arguments will be
passed as arguments in order to the next task.
* `callback(err, [results])` - An optional callback to run once all the functions
have completed. This will be passed the results of the last task's callback.
@@ -914,19 +971,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'
});
```
@@ -973,13 +1030,14 @@ 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.
__Arguments__
-* functions... - the asynchronous functions to compose
+* `functions...` - the asynchronous functions to compose
__Example__
@@ -987,31 +1045,23 @@ __Example__
```js
// Requires lodash (or underscore), express3 and dresende's orm2.
// Part of an app, that fetches cats of the logged user.
-// This example uses `seq` function to avoid overnesting and error
+// 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);
- }
+ });
});
```
@@ -1019,7 +1069,7 @@ app.get('/cats', function(request, response) {
<a name="applyEach" />
### applyEach(fns, args..., callback)
-Applies the provided arguments to each function in the array, calling
+Applies the provided arguments to each function in the array, calling
`callback` after all functions have completed. If you only provide the first
argument, then it will return a function which lets you pass in the
arguments as if it were a single function call.
@@ -1045,30 +1095,27 @@ async.each(
);
```
----------------------------------------
+__Related__
-<a name="applyEachSeries" />
-### applyEachSeries(arr, iterator, callback)
-
-The same as [`applyEach`](#applyEach) only the functions are applied in series.
+* applyEachSeries(tasks, args..., [callback])
---------------------------------------
<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
-`worker`s are in progress, the task is queued until one becomes available.
+`worker`s are in progress, the task is queued until one becomes available.
Once a `worker` completes a `task`, that `task`'s callback is called.
__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.
+ task, which must call its `callback(err)` argument when finished, with an
+ 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__
@@ -1078,22 +1125,23 @@ methods:
* `length()` - a function returning the number of items waiting to be processed.
* `started` - a function returning whether or not any items have been pushed and processed by the queue
* `running()` - a function returning the number of items currently being processed.
+* `workersList()` - a function returning the array of items currently being processed.
* `idle()` - a function returning false if there are items waiting or being processed, or true if not.
* `concurrency` - an integer for determining how many `worker` functions should be
run in parallel. This property can be changed after a `queue` is created to
alter the concurrency on-the-fly.
-* `push(task, [callback])` - add a new task to the `queue`. Calls `callback` once
+* `push(task, [callback])` - add a new task to the `queue`. Calls `callback` once
the `worker` has finished processing the task. Instead of a single task, a `tasks` array
can be submitted. The respective callback is used for every task in the list.
* `unshift(task, [callback])` - add a new task to the front of the `queue`.
-* `saturated` - a callback that is called when the `queue` length hits the `concurrency` limit,
+* `saturated` - a callback that is called when the `queue` length hits the `concurrency` limit,
and further tasks will be queued.
* `empty` - a callback that is called when the last item from the `queue` is given to a `worker`.
* `drain` - a callback that is called when the last item from the `queue` has returned from the `worker`.
* `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__
@@ -1123,7 +1171,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
@@ -1154,7 +1202,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
@@ -1163,7 +1211,7 @@ when the worker is finished.
__Arguments__
* `worker(tasks, callback)` - An asynchronous function for processing an array of
- queued tasks, which must call its `callback(err)` argument when finished, with
+ queued tasks, which must call its `callback(err)` argument when finished, with
an optional `err` argument.
* `payload` - An optional `integer` for determining how many tasks should be
processed per round; if omitted, the default is unlimited.
@@ -1178,11 +1226,12 @@ methods:
process per round. This property can be changed after a `cargo` is created to
alter the payload on-the-fly.
* `push(task, [callback])` - Adds `task` to the `queue`. The callback is called
- once the `worker` has finished processing the task. Instead of a single task, an array of `tasks`
+ once the `worker` has finished processing the task. Instead of a single task, an array of `tasks`
can be submitted. The respective callback is used for every task in the list.
* `saturated` - A callback that is called when the `queue.length()` hits the concurrency and further tasks will be queued.
* `empty` - A callback that is called when the last item from the `queue` is given to a `worker`.
* `drain` - A callback that is called when the last item from the `queue` has returned from the `worker`.
+* `idle()`, `pause()`, `resume()`, `kill()` - cargo inherits all of the same methods and event calbacks as [`queue`](#queue)
__Example__
@@ -1213,20 +1262,15 @@ cargo.push({name: 'baz'}, function (err) {
---------------------------------------
<a name="auto" />
-### auto(tasks, [callback])
+### auto(tasks, [concurrency], [callback])
-Determines the best order for running the functions in `tasks`, based on their
-requirements. Each function can optionally depend on other functions being completed
-first, and each function is run as soon as its requirements are satisfied.
+Determines the best order for running the functions in `tasks`, based on their requirements. Each function can optionally depend on other functions being completed first, and each function is run as soon as its requirements are satisfied.
-If any of the functions pass an error to their callback, it will not
-complete (so any other functions depending on it will not run), and the main
-`callback` is immediately called with the error. Functions also receive an
-object containing the results of functions which have completed so far.
+If any of the functions pass an error to their callback, the `auto` sequence will stop. Further tasks will not execute (so any other functions depending on it will not run), and the main `callback` is immediately called with the error. Functions also receive an object containing the results of functions which have completed so far.
-Note, all functions are called with a `results` object as a second argument,
+Note, all functions are called with a `results` object as a second argument,
so it is unsafe to pass functions in the `tasks` object which cannot handle the
-extra argument.
+extra argument.
For example, this snippet of code:
@@ -1243,7 +1287,7 @@ argument, which will fail:
fs.readFile('data.txt', 'utf-8', cb, {});
```
-Instead, wrap the call to `readFile` in a function which does not forward the
+Instead, wrap the call to `readFile` in a function which does not forward the
`results` object:
```js
@@ -1260,13 +1304,14 @@ __Arguments__
requirements, with the function itself the last item in the array. The object's key
of a property serves as the name of the task defined by that property,
i.e. can be used when specifying requirements for other tasks.
- The function receives two arguments: (1) a `callback(err, result)` which must be
- called when finished, passing an `error` (which can be `null`) and the result of
+ The function receives two arguments: (1) a `callback(err, result)` which must be
+ called when finished, passing an `error` (which can be `null`) and the result of
the function's execution, and (2) a `results` object, containing the results of
the previously executed functions.
+* `concurrency` - An optional `integer` for determining the maximum number of tasks that can be run in parallel. By default, as many as possible.
* `callback(err, results)` - An optional callback which is called when all the
- tasks have been completed. It receives the `err` argument if any `tasks`
- pass an error to their callback. Results are always returned; however, if
+ tasks have been completed. It receives the `err` argument if any `tasks`
+ pass an error to their callback. Results are always returned; however, if
an error occurs, no further `tasks` will be performed, and the results
object will only contain partial results.
@@ -1413,18 +1458,19 @@ async.autoInject({
---------------------------------------
<a name="retry" />
-### retry([times = 5], task, [callback])
+### retry([opts = {times: 5, interval: 0}| 5], task, [callback])
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__
-* `times` - An integer indicating how many times to attempt the `task` before giving up. Defaults to 5.
+* `opts` - Can be either an object with `times` and `interval` or a number. `times` is how many attempts should be made before giving up. `interval` is how long to wait inbetween attempts. Defaults to {times: 5, interval: 0}
+ * if a number is passed in it sets `times` only (with `interval` defaulting to 0).
* `task(callback, results)` - A function which receives two arguments: (1) a `callback(err, result)`
- which must be called when finished, passing `err` (which can be `null`) and the `result` of
+ which must be called when finished, passing `err` (which can be `null`) and the `result` of
the function's execution, and (2) a `results` object, containing the results of
the previously executed functions (if nested inside another control flow).
* `callback(err, results)` - An optional callback which is called when the
@@ -1439,7 +1485,13 @@ async.retry(3, apiMethod, function(err, result) {
});
```
-It can also be embeded within other control flow functions to retry individual methods
+```js
+async.retry({times: 3, interval: 200}, apiMethod, function(err, result) {
+ // do something with the result
+});
+```
+
+It can also be embedded within other control flow functions to retry individual methods
that are not as reliable, like this:
```js
@@ -1493,7 +1545,7 @@ node> nextfn();
<a name="apply" />
### apply(function, arguments..)
-Creates a continuation function with some arguments already applied.
+Creates a continuation function with some arguments already applied.
Useful as a shorthand when combined with other control flow functions. Any arguments
passed to the returned function are added to the arguments originally passed
@@ -1542,7 +1594,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)`
@@ -1567,15 +1619,16 @@ call_order.push('one')
```
<a name="times" />
-### times(n, callback)
+### times(n, iterator, [callback])
-Calls the `callback` function `n` times, and accumulates results in the same manner
+Calls the `iterator` function `n` times, and accumulates results in the same manner
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__
@@ -1596,12 +1649,10 @@ async.times(5, function(n, next){
});
```
-<a name="timesSeries" />
-### timesSeries(n, callback)
+__Related__
-The same as [`times`](#times), only the iterator is applied to each item in `arr` in
-series. The next `iterator` is only called once the current one has completed.
-The results array will be in the same order as the original.
+* timesSeries(n, iterator, [callback])
+* timesLimit(n, limit, iterator, [callback])
## Utils
@@ -1613,13 +1664,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.
@@ -1648,6 +1701,102 @@ __Arguments__
* `fn` - the memoized function
+---------------------------------------
+
+<a name="ensureAsync" />
+### ensureAsync(fn)
+
+Wrap an async function and ensure it calls its callback on a later tick of the event loop. If the function already calls its callback on a next tick, no extra deferral is added. This is useful for preventing stack overflows (`RangeError: Maximum call stack size exceeded`) and generally keeping [Zalgo](http://blog.izs.me/post/59142742143/designing-apis-for-asynchrony) contained.
+
+__Arguments__
+
+* `fn` - an async function, one that expects a node-style callback as its last argument
+
+Returns a wrapped function with the exact same call signature as the function passed in.
+
+__Example__
+
+```js
+function sometimesAsync(arg, callback) {
+ if (cache[arg]) {
+ return callback(null, cache[arg]); // this would be synchronous!!
+ } else {
+ doSomeIO(arg, callback); // this IO would be asynchronous
+ }
+}
+
+// this has a risk of stack overflows if many results are cached in a row
+async.mapSeries(args, sometimesAsync, done);
+
+// this will defer sometimesAsync's callback if necessary,
+// preventing stack overflows
+async.mapSeries(args, async.ensureAsync(sometimesAsync), done);
+
+```
+
+---------------------------------------
+
+<a name="constant">
+### constant(values...)
+
+Returns a function that when called, calls-back with the values provided. Useful as the first function in a `waterfall`, or for plugging values in to `auto`.
+
+__Example__
+
+```js
+async.waterfall([
+ async.constant(42),
+ function (value, next) {
+ // value === 42
+ },
+ //...
+], callback);
+
+async.waterfall([
+ async.constant(filename, "utf8"),
+ fs.readFile,
+ function (fileData, next) {
+ //...
+ }
+ //...
+], callback);
+
+async.auto({
+ hostname: async.constant("https://server.net/"),
+ port: findFreePort,
+ launchServer: ["hostname", "port", function (cb, options) {
+ startServer(options, cb);
+ }],
+ //...
+}, callback);
+
+```
+
+---------------------------------------
+
+<a name="asyncify">
+<a name="wrapSync">
+### asyncify(func)
+
+__Alias:__ `wrapSync`
+
+Take a sync function and make it async, passing its return value to a callback. This is useful for plugging sync functions into a waterfall, series, or other async functions. Any arguments passed to the generated function will be passed to the wrapped function (except for the final callback argument). Errors thrown will be passed to the callback.
+
+__Example__
+
+```js
+async.waterfall([
+ async.apply(fs.readFile, filename, "utf8"),
+ async.asyncify(JSON.parse),
+ function (data, next) {
+ // data is the result of parsing the text.
+ // If there was a parsing error, it would have been caught.
+ }
+], callback)
+```
+
+---------------------------------------
+
<a name="log" />
### log(function, arguments)
diff --git a/bower.json b/bower.json
new file mode 100644
index 0000000..7a5b5e6
--- /dev/null
+++ b/bower.json
@@ -0,0 +1,57 @@
+{
+ "name": "async",
+ "description": "Higher-order functions and common patterns for asynchronous code",
+ "main": "lib/async.js",
+ "keywords": [
+ "async",
+ "callback",
+ "utility",
+ "module"
+ ],
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/caolan/async.git"
+ },
+ "devDependencies": {
+ "benchmark": "bestiejs/benchmark.js",
+ "bluebird": "^2.9.32",
+ "chai": "^3.1.0",
+ "coveralls": "^2.11.2",
+ "es6-promise": "^2.3.0",
+ "jscs": "^1.13.1",
+ "jshint": "~2.8.0",
+ "karma": "^0.13.2",
+ "karma-browserify": "^4.2.1",
+ "karma-firefox-launcher": "^0.1.6",
+ "karma-mocha": "^0.2.0",
+ "karma-mocha-reporter": "^1.0.2",
+ "lodash": "^3.9.0",
+ "mkdirp": "~0.5.1",
+ "mocha": "^2.2.5",
+ "native-promise-only": "^0.8.0-a",
+ "nodeunit": ">0.0.0",
+ "nyc": "^2.1.0",
+ "rsvp": "^3.0.18",
+ "semver": "^4.3.6",
+ "uglify-js": "~2.4.0",
+ "xyz": "^0.5.0",
+ "yargs": "~3.9.1"
+ },
+ "moduleType": [
+ "amd",
+ "globals",
+ "node"
+ ],
+ "ignore": [
+ "**/.*",
+ "node_modules",
+ "bower_components",
+ "test",
+ "tests"
+ ],
+ "authors": [
+ "Caolan McMahon"
+ ],
+ "version": "1.5.1"
+}
diff --git a/component.json b/component.json
index bbb0115..a5d77f4 100644
--- a/component.json
+++ b/component.json
@@ -1,11 +1,17 @@
{
"name": "async",
- "repo": "caolan/async",
"description": "Higher-order functions and common patterns for asynchronous code",
- "version": "0.1.23",
- "keywords": [],
- "dependencies": {},
- "development": {},
+ "version": "1.5.1",
+ "keywords": [
+ "async",
+ "callback",
+ "utility",
+ "module"
+ ],
+ "license": "MIT",
"main": "lib/async.js",
- "scripts": [ "lib/async.js" ]
+ "repository": "caolan/async",
+ "scripts": [
+ "lib/async.js"
+ ]
}
diff --git a/deps/nodeunit.js b/deps/nodeunit.js
index 5957184..6251ba1 100644
--- a/deps/nodeunit.js
+++ b/deps/nodeunit.js
@@ -171,9 +171,7 @@ nodeunit = (function(){
// Create a JSON object only if one does not already exist. We create the
// methods in a closure to avoid creating global variables.
-if (!this.JSON) {
- this.JSON = {};
-}
+var JSON = {};
(function () {
"use strict";
@@ -579,14 +577,19 @@ var reporter = {};
//// exported async module functions ////
//// nextTick implementation with browser-compatible fallback ////
- async.nextTick = function (fn) {
- if (typeof process === 'undefined' || !(process.nextTick)) {
+ if (typeof setImmediate === 'function') {
+ async.nextTick = function (fn) {
+ setImmediate(fn);
+ };
+ }
+ else if (typeof process !== 'undefined' && process.nextTick) {
+ async.nextTick = process.nextTick;
+ }
+ else {
+ async.nextTick = function (fn) {
setTimeout(fn, 0);
- }
- else {
- process.nextTick(fn);
- }
- };
+ };
+ }
async.forEach = function (arr, iterator, callback) {
if (!arr.length) {
@@ -1099,6 +1102,27 @@ var reporter = {};
async.warn = _console_fn('warn');
async.error = _console_fn('error');*/
+ async.memoize = function (fn, hasher) {
+ var memo = {};
+ hasher = hasher || function (x) {
+ return x;
+ };
+ return function () {
+ var args = Array.prototype.slice.call(arguments);
+ var callback = args.pop();
+ var key = hasher.apply(null, args);
+ if (key in memo) {
+ callback.apply(null, memo[key]);
+ }
+ else {
+ fn.apply(null, args.concat([function () {
+ memo[key] = arguments;
+ callback.apply(null, arguments);
+ }]));
+ }
+ };
+ };
+
}());
(function(exports){
/**
@@ -1114,6 +1138,9 @@ var reporter = {};
var _keys = function(obj){
if(Object.keys) return Object.keys(obj);
+ if (typeof obj != 'object' && typeof obj != 'function') {
+ throw new TypeError('-');
+ }
var keys = [];
for(var k in obj){
if(obj.hasOwnProperty(k)) keys.push(k);
@@ -1187,9 +1214,9 @@ assert.AssertionError.prototype.toString = function() {
return [this.name+":", this.message].join(' ');
} else {
return [ this.name+":"
- , JSON.stringify(this.expected )
+ , typeof this.expected !== 'undefined' ? JSON.stringify(this.expected) : 'undefined'
, this.operator
- , JSON.stringify(this.actual)
+ , typeof this.actual !== 'undefined' ? JSON.stringify(this.actual) : 'undefined'
].join(" ");
}
};
@@ -1259,6 +1286,17 @@ assert.deepEqual = function deepEqual(actual, expected, message) {
}
};
+var Buffer = null;
+if (typeof require !== 'undefined' && typeof process !== 'undefined') {
+ try {
+ Buffer = require('buffer').Buffer;
+ }
+ catch (e) {
+ // May be a CommonJS environment other than Node.js
+ Buffer = null;
+ }
+}
+
function _deepEqual(actual, expected) {
// 7.1. All identical values are equivalent, as determined by ===.
if (actual === expected) {
@@ -1268,6 +1306,25 @@ function _deepEqual(actual, expected) {
} else if (actual instanceof Date && expected instanceof Date) {
return actual.getTime() === expected.getTime();
+ // 7.2.1 If the expcted value is a RegExp object, the actual value is
+ // equivalent if it is also a RegExp object that refers to the same source and options
+ } else if (actual instanceof RegExp && expected instanceof RegExp) {
+ return actual.source === expected.source &&
+ actual.global === expected.global &&
+ actual.ignoreCase === expected.ignoreCase &&
+ actual.multiline === expected.multiline;
+
+ } else if (Buffer && actual instanceof Buffer && expected instanceof Buffer) {
+ return (function() {
+ var i, len;
+
+ for (i = 0, len = expected.length; i < len; i++) {
+ if (actual[i] !== expected[i]) {
+ return false;
+ }
+ }
+ return actual.length === expected.length;
+ })();
// 7.3. Other pairs that do not both pass typeof value == "object",
// equivalence is determined by ==.
} else if (typeof actual != 'object' && typeof expected != 'object') {
@@ -1362,47 +1419,52 @@ assert.notStrictEqual = function notStrictEqual(actual, expected, message) {
}
};
-function _throws (shouldThrow, block, err, message) {
- var exception = null,
- threw = false,
- typematters = true;
+function expectedException(actual, expected) {
+ if (!actual || !expected) {
+ return false;
+ }
- message = message || "";
+ if (expected instanceof RegExp) {
+ return expected.test(actual.message || actual);
+ } else if (actual instanceof expected) {
+ return true;
+ } else if (expected.call({}, actual) === true) {
+ return true;
+ }
- //handle optional arguments
- if (arguments.length == 3) {
- if (typeof(err) == "string") {
- message = err;
- typematters = false;
- }
- } else if (arguments.length == 2) {
- typematters = false;
+ return false;
+}
+
+function _throws(shouldThrow, block, expected, message) {
+ var actual;
+
+ if (typeof expected === 'string') {
+ message = expected;
+ expected = null;
}
try {
block();
} catch (e) {
- threw = true;
- exception = e;
+ actual = e;
}
- if (shouldThrow && !threw) {
- fail( "Missing expected exception"
- + (err && err.name ? " ("+err.name+")." : '.')
- + (message ? " " + message : "")
- );
+ message = (expected && expected.name ? ' (' + expected.name + ').' : '.') +
+ (message ? ' ' + message : '.');
+
+ if (shouldThrow && !actual) {
+ fail('Missing expected exception' + message);
}
- if (!shouldThrow && threw && typematters && exception instanceof err) {
- fail( "Got unwanted exception"
- + (err && err.name ? " ("+err.name+")." : '.')
- + (message ? " " + message : "")
- );
+
+ if (!shouldThrow && expectedException(actual, expected)) {
+ fail('Got unwanted exception' + message);
}
- if ((shouldThrow && threw && typematters && !(exception instanceof err)) ||
- (!shouldThrow && threw)) {
- throw exception;
+
+ if ((shouldThrow && actual && expected &&
+ !expectedException(actual, expected)) || (!shouldThrow && actual)) {
+ throw actual;
}
-};
+}
// 11. Expected to throw an error:
// assert.throws(block, Error_opt, message_opt);
@@ -1425,7 +1487,7 @@ assert.ifError = function (err) { if (err) {throw err;}};
* MIT Licensed
*
* THIS FILE SHOULD BE BROWSER-COMPATIBLE JS!
- * Only code on that line will be removed, its mostly to avoid requiring code
+ * Only code on that line will be removed, it's mostly to avoid requiring code
* that is node specific
*/
@@ -1470,8 +1532,10 @@ exports.assertionList = function (arr, duration) {
var that = arr || [];
that.failures = function () {
var failures = 0;
- for (var i=0; i<this.length; i++) {
- if (this[i].failed()) failures++;
+ for (var i = 0; i < this.length; i += 1) {
+ if (this[i].failed()) {
+ failures += 1;
+ }
}
return failures;
};
@@ -1484,7 +1548,7 @@ exports.assertionList = function (arr, duration) {
/**
* Create a wrapper function for assert module methods. Executes a callback
- * after the it's complete with an assertion object representing the result.
+ * after it's complete with an assertion object representing the result.
*
* @param {Function} callback
* @api private
@@ -1493,7 +1557,7 @@ exports.assertionList = function (arr, duration) {
var assertWrapper = function (callback) {
return function (new_method, assert_method, arity) {
return function () {
- var message = arguments[arity-1];
+ var message = arguments[arity - 1];
var a = exports.assertion({method: new_method, message: message});
try {
assert[assert_method].apply(null, arguments);
@@ -1596,6 +1660,7 @@ exports.options = function (opt) {
optionalCallback('moduleStart');
optionalCallback('moduleDone');
optionalCallback('testStart');
+ optionalCallback('testReady');
optionalCallback('testDone');
//optionalCallback('log');
@@ -1611,7 +1676,7 @@ exports.options = function (opt) {
* MIT Licensed
*
* THIS FILE SHOULD BE BROWSER-COMPATIBLE JS!
- * Only code on that line will be removed, its mostly to avoid requiring code
+ * Only code on that line will be removed, it's mostly to avoid requiring code
* that is node specific
*/
@@ -1625,16 +1690,30 @@ exports.options = function (opt) {
* Added for browser compatibility
*/
-var _keys = function(obj){
- if(Object.keys) return Object.keys(obj);
+var _keys = function (obj) {
+ if (Object.keys) {
+ return Object.keys(obj);
+ }
var keys = [];
- for(var k in obj){
- if(obj.hasOwnProperty(k)) keys.push(k);
+ for (var k in obj) {
+ if (obj.hasOwnProperty(k)) {
+ keys.push(k);
+ }
}
return keys;
};
+var _copy = function (obj) {
+ var nobj = {};
+ var keys = _keys(obj);
+ for (var i = 0; i < keys.length; i += 1) {
+ nobj[keys[i]] = obj[keys[i]];
+ }
+ return nobj;
+};
+
+
/**
* Runs a test function (fn) from a loaded module. After the test function
* calls test.done(), the callback is executed with an assertionList as its
@@ -1654,6 +1733,7 @@ exports.runTest = function (name, fn, opt, callback) {
var start = new Date().getTime();
var test = types.test(name, start, options, callback);
+ options.testReady(test);
try {
fn(test);
}
@@ -1678,20 +1758,36 @@ exports.runTest = function (name, fn, opt, callback) {
*/
exports.runSuite = function (name, suite, opt, callback) {
+ suite = wrapGroup(suite);
var keys = _keys(suite);
async.concatSeries(keys, function (k, cb) {
var prop = suite[k], _name;
_name = name ? [].concat(name, k) : [k];
-
_name.toString = function () {
// fallback for old one
return this.join(' - ');
};
if (typeof prop === 'function') {
- exports.runTest(_name, suite[k], opt, cb);
+ var in_name = false,
+ in_specific_test = (_name.toString() === opt.testFullSpec) ? true : false;
+ for (var i = 0; i < _name.length; i += 1) {
+ if (_name[i] === opt.testspec) {
+ in_name = true;
+ }
+ }
+
+ if ((!opt.testFullSpec || in_specific_test) && (!opt.testspec || in_name)) {
+ if (opt.moduleStart) {
+ opt.moduleStart();
+ }
+ exports.runTest(_name, suite[k], opt, cb);
+ }
+ else {
+ return cb();
+ }
}
else {
exports.runSuite(_name, suite[k], opt, cb);
@@ -1710,15 +1806,30 @@ exports.runSuite = function (name, suite, opt, callback) {
*/
exports.runModule = function (name, mod, opt, callback) {
- var options = types.options(opt);
+ var options = _copy(types.options(opt));
+
+ var _run = false;
+ var _moduleStart = options.moduleStart;
+
+ mod = wrapGroup(mod);
+
+ function run_once() {
+ if (!_run) {
+ _run = true;
+ _moduleStart(name);
+ }
+ }
+ options.moduleStart = run_once;
- options.moduleStart(name);
var start = new Date().getTime();
- exports.runSuite(null, mod, opt, function (err, a_list) {
+ exports.runSuite(null, mod, options, function (err, a_list) {
var end = new Date().getTime();
var assertion_list = types.assertionList(a_list, end - start);
options.moduleDone(name, assertion_list);
+ if (nodeunit.complete) {
+ nodeunit.complete(name, assertion_list);
+ }
callback(null, a_list);
});
};
@@ -1792,7 +1903,34 @@ var wrapTest = function (setUp, tearDown, fn) {
else {
fn.call(context, test);
}
+ };
+};
+
+
+/**
+ * Returns a serial callback from two functions.
+ *
+ * @param {Function} funcFirst
+ * @param {Function} funcSecond
+ * @api private
+ */
+
+var getSerialCallback = function (fns) {
+ if (!fns.length) {
+ return null;
}
+ return function (callback) {
+ var that = this;
+ var bound_fns = [];
+ for (var i = 0, len = fns.length; i < len; i++) {
+ (function (j) {
+ bound_fns.push(function () {
+ return fns[j].apply(that, arguments);
+ });
+ })(i);
+ }
+ return async.series(bound_fns, callback);
+ };
};
@@ -1800,43 +1938,52 @@ var wrapTest = function (setUp, tearDown, fn) {
* Wraps a group of tests with setUp and tearDown functions.
* Used by testCase.
*
- * @param {Function} setUp
- * @param {Function} tearDown
* @param {Object} group
+ * @param {Array} setUps - parent setUp functions
+ * @param {Array} tearDowns - parent tearDown functions
* @api private
*/
-var wrapGroup = function (setUp, tearDown, group) {
+var wrapGroup = function (group, setUps, tearDowns) {
var tests = {};
+
+ var setUps = setUps ? setUps.slice(): [];
+ var tearDowns = tearDowns ? tearDowns.slice(): [];
+
+ if (group.setUp) {
+ setUps.push(group.setUp);
+ delete group.setUp;
+ }
+ if (group.tearDown) {
+ tearDowns.unshift(group.tearDown);
+ delete group.tearDown;
+ }
+
var keys = _keys(group);
- for (var i=0; i<keys.length; i++) {
+
+ for (var i = 0; i < keys.length; i += 1) {
var k = keys[i];
if (typeof group[k] === 'function') {
- tests[k] = wrapTest(setUp, tearDown, group[k]);
+ tests[k] = wrapTest(
+ getSerialCallback(setUps),
+ getSerialCallback(tearDowns),
+ group[k]
+ );
}
else if (typeof group[k] === 'object') {
- tests[k] = wrapGroup(setUp, tearDown, group[k]);
+ tests[k] = wrapGroup(group[k], setUps, tearDowns);
}
}
return tests;
-}
+};
/**
- * Utility for wrapping a suite of test functions with setUp and tearDown
- * functions.
- *
- * @param {Object} suite
- * @return {Object}
- * @api public
+ * Backwards compatibility for test suites using old testCase API
*/
exports.testCase = function (suite) {
- var setUp = suite.setUp;
- var tearDown = suite.tearDown;
- delete suite.setUp;
- delete suite.tearDown;
- return wrapGroup(setUp, tearDown, suite);
+ return suite;
};
})(core);
(function(exports){
@@ -1871,8 +2018,10 @@ exports.info = "Browser-based test reporter";
* @api public
*/
-exports.run = function (modules, options) {
- var start = new Date().getTime();
+exports.run = function (modules, options, callback) {
+ var start = new Date().getTime(), div;
+ options = options || {};
+ div = options.div || document.body;
function setText(el, txt) {
if ('innerText' in el) {
@@ -1888,7 +2037,7 @@ exports.run = function (modules, options) {
if (!el) {
el = document.createElement(tag);
el.id = id;
- document.body.appendChild(el);
+ div.appendChild(el);
}
return el;
};
@@ -1955,6 +2104,8 @@ exports.run = function (modules, options) {
assertions.passes() + '</span> assertions of ' +
'<span class="all">' + assertions.length + '<span> passed, ' +
assertions.failures() + ' failed.';
+
+ if (callback) callback(assertions.failures() ? new Error('We have got test failures.') : undefined);
}
});
};
diff --git a/dist/async.js b/dist/async.js
new file mode 100644
index 0000000..9ecbda6
--- /dev/null
+++ b/dist/async.js
@@ -0,0 +1,1264 @@
+/*!
+ * async
+ * https://github.com/caolan/async
+ *
+ * Copyright 2010-2014 Caolan McMahon
+ * Released under the MIT license
+ */
+(function () {
+
+ var async = {};
+ function noop() {}
+ function identity(v) {
+ return v;
+ }
+ function toBool(v) {
+ return !!v;
+ }
+ function notId(v) {
+ return !v;
+ }
+
+ // global on the server, window in the browser
+ var previous_async;
+
+ // Establish the root object, `window` (`self`) in the browser, `global`
+ // on the server, or `this` in some virtual machines. We use `self`
+ // instead of `window` for `WebWorker` support.
+ var root = typeof self === 'object' && self.self === self && self ||
+ typeof global === 'object' && global.global === global && global ||
+ this;
+
+ if (root != null) {
+ previous_async = root.async;
+ }
+
+ async.noConflict = function () {
+ root.async = previous_async;
+ return async;
+ };
+
+ function only_once(fn) {
+ return function() {
+ if (fn === null) throw new Error("Callback was already called.");
+ fn.apply(this, arguments);
+ fn = null;
+ };
+ }
+
+ function _once(fn) {
+ return function() {
+ if (fn === null) return;
+ fn.apply(this, arguments);
+ fn = null;
+ };
+ }
+
+ //// cross-browser compatiblity functions ////
+
+ var _toString = Object.prototype.toString;
+
+ var _isArray = Array.isArray || function (obj) {
+ return _toString.call(obj) === '[object Array]';
+ };
+
+ // Ported from underscore.js isObject
+ var _isObject = function(obj) {
+ var type = typeof obj;
+ return type === 'function' || type === 'object' && !!obj;
+ };
+
+ function _isArrayLike(arr) {
+ return _isArray(arr) || (
+ // has a positive integer length property
+ typeof arr.length === "number" &&
+ arr.length >= 0 &&
+ arr.length % 1 === 0
+ );
+ }
+
+ function _arrayEach(arr, iterator) {
+ var index = -1,
+ length = arr.length;
+
+ while (++index < length) {
+ iterator(arr[index], index, arr);
+ }
+ }
+
+ function _map(arr, iterator) {
+ var index = -1,
+ length = arr.length,
+ result = Array(length);
+
+ while (++index < length) {
+ result[index] = iterator(arr[index], index, arr);
+ }
+ return result;
+ }
+
+ function _range(count) {
+ return _map(Array(count), function (v, i) { return i; });
+ }
+
+ function _reduce(arr, iterator, memo) {
+ _arrayEach(arr, function (x, i, a) {
+ memo = iterator(memo, x, i, a);
+ });
+ return memo;
+ }
+
+ function _forEachOf(object, iterator) {
+ _arrayEach(_keys(object), function (key) {
+ iterator(object[key], key);
+ });
+ }
+
+ function _indexOf(arr, item) {
+ for (var i = 0; i < arr.length; i++) {
+ if (arr[i] === item) return i;
+ }
+ return -1;
+ }
+
+ var _keys = Object.keys || function (obj) {
+ var keys = [];
+ for (var k in obj) {
+ if (obj.hasOwnProperty(k)) {
+ keys.push(k);
+ }
+ }
+ return keys;
+ };
+
+ function _keyIterator(coll) {
+ var i = -1;
+ var len;
+ var keys;
+ if (_isArrayLike(coll)) {
+ len = coll.length;
+ return function next() {
+ i++;
+ return i < len ? i : null;
+ };
+ } else {
+ keys = _keys(coll);
+ len = keys.length;
+ return function next() {
+ i++;
+ return i < len ? keys[i] : null;
+ };
+ }
+ }
+
+ // Similar to ES6's rest param (http://ariya.ofilabs.com/2013/03/es6-and-rest-parameter.html)
+ // This accumulates the arguments passed into an array, after a given index.
+ // From underscore.js (https://github.com/jashkenas/underscore/pull/2140).
+ function _restParam(func, startIndex) {
+ startIndex = startIndex == null ? func.length - 1 : +startIndex;
+ return function() {
+ var length = Math.max(arguments.length - startIndex, 0);
+ var rest = Array(length);
+ for (var index = 0; index < length; index++) {
+ rest[index] = arguments[index + startIndex];
+ }
+ switch (startIndex) {
+ case 0: return func.call(this, rest);
+ case 1: return func.call(this, arguments[0], rest);
+ }
+ // Currently unused but handle cases outside of the switch statement:
+ // var args = Array(startIndex + 1);
+ // for (index = 0; index < startIndex; index++) {
+ // args[index] = arguments[index];
+ // }
+ // args[startIndex] = rest;
+ // return func.apply(this, args);
+ };
+ }
+
+ function _withoutIndex(iterator) {
+ return function (value, index, callback) {
+ return iterator(value, callback);
+ };
+ }
+
+ //// exported async module functions ////
+
+ //// nextTick implementation with browser-compatible fallback ////
+
+ // capture the global reference to guard against fakeTimer mocks
+ var _setImmediate = typeof setImmediate === 'function' && setImmediate;
+
+ var _delay = _setImmediate ? function(fn) {
+ // not a direct alias for IE10 compatibility
+ _setImmediate(fn);
+ } : function(fn) {
+ setTimeout(fn, 0);
+ };
+
+ if (typeof process === 'object' && typeof process.nextTick === 'function') {
+ async.nextTick = process.nextTick;
+ } else {
+ async.nextTick = _delay;
+ }
+ async.setImmediate = _setImmediate ? _delay : async.nextTick;
+
+
+ async.forEach =
+ async.each = function (arr, iterator, callback) {
+ return async.eachOf(arr, _withoutIndex(iterator), callback);
+ };
+
+ async.forEachSeries =
+ async.eachSeries = function (arr, iterator, callback) {
+ return async.eachOfSeries(arr, _withoutIndex(iterator), callback);
+ };
+
+
+ async.forEachLimit =
+ async.eachLimit = function (arr, limit, iterator, callback) {
+ return _eachOfLimit(limit)(arr, _withoutIndex(iterator), callback);
+ };
+
+ async.forEachOf =
+ async.eachOf = function (object, iterator, callback) {
+ callback = _once(callback || noop);
+ object = object || [];
+
+ var iter = _keyIterator(object);
+ var key, completed = 0;
+
+ while ((key = iter()) != null) {
+ completed += 1;
+ iterator(object[key], key, only_once(done));
+ }
+
+ if (completed === 0) callback(null);
+
+ function done(err) {
+ completed--;
+ if (err) {
+ callback(err);
+ }
+ // Check key is null in case iterator isn't exhausted
+ // and done resolved synchronously.
+ else if (key === null && completed <= 0) {
+ callback(null);
+ }
+ }
+ };
+
+ async.forEachOfSeries =
+ async.eachOfSeries = function (obj, iterator, callback) {
+ callback = _once(callback || noop);
+ obj = obj || [];
+ var nextKey = _keyIterator(obj);
+ var key = nextKey();
+ function iterate() {
+ var sync = true;
+ if (key === null) {
+ return callback(null);
+ }
+ iterator(obj[key], key, only_once(function (err) {
+ if (err) {
+ callback(err);
+ }
+ else {
+ key = nextKey();
+ if (key === null) {
+ return callback(null);
+ } else {
+ if (sync) {
+ async.setImmediate(iterate);
+ } else {
+ iterate();
+ }
+ }
+ }
+ }));
+ sync = false;
+ }
+ iterate();
+ };
+
+
+
+ async.forEachOfLimit =
+ async.eachOfLimit = function (obj, limit, iterator, callback) {
+ _eachOfLimit(limit)(obj, iterator, callback);
+ };
+
+ function _eachOfLimit(limit) {
+
+ return function (obj, iterator, callback) {
+ callback = _once(callback || noop);
+ obj = obj || [];
+ var nextKey = _keyIterator(obj);
+ if (limit <= 0) {
+ return callback(null);
+ }
+ var done = false;
+ var running = 0;
+ var errored = false;
+
+ (function replenish () {
+ if (done && running <= 0) {
+ return callback(null);
+ }
+
+ while (running < limit && !errored) {
+ var key = nextKey();
+ if (key === null) {
+ done = true;
+ if (running <= 0) {
+ callback(null);
+ }
+ return;
+ }
+ running += 1;
+ iterator(obj[key], key, only_once(function (err) {
+ running -= 1;
+ if (err) {
+ callback(err);
+ errored = true;
+ }
+ else {
+ replenish();
+ }
+ }));
+ }
+ })();
+ };
+ }
+
+
+ function doParallel(fn) {
+ return function (obj, iterator, callback) {
+ return fn(async.eachOf, obj, iterator, callback);
+ };
+ }
+ function doParallelLimit(fn) {
+ return function (obj, limit, iterator, callback) {
+ return fn(_eachOfLimit(limit), obj, iterator, callback);
+ };
+ }
+ function doSeries(fn) {
+ return function (obj, iterator, callback) {
+ return fn(async.eachOfSeries, obj, iterator, callback);
+ };
+ }
+
+ function _asyncMap(eachfn, arr, iterator, callback) {
+ callback = _once(callback || noop);
+ arr = arr || [];
+ var results = _isArrayLike(arr) ? [] : {};
+ eachfn(arr, function (value, index, callback) {
+ iterator(value, function (err, v) {
+ results[index] = v;
+ callback(err);
+ });
+ }, function (err) {
+ callback(err, results);
+ });
+ }
+
+ async.map = doParallel(_asyncMap);
+ async.mapSeries = doSeries(_asyncMap);
+ async.mapLimit = doParallelLimit(_asyncMap);
+
+ // reduce only has a series version, as doing reduce in parallel won't
+ // work in many situations.
+ async.inject =
+ async.foldl =
+ async.reduce = function (arr, memo, iterator, callback) {
+ async.eachOfSeries(arr, function (x, i, callback) {
+ iterator(memo, x, function (err, v) {
+ memo = v;
+ callback(err);
+ });
+ }, function (err) {
+ callback(err, memo);
+ });
+ };
+
+ async.foldr =
+ async.reduceRight = function (arr, memo, iterator, callback) {
+ var reversed = _map(arr, identity).reverse();
+ async.reduce(reversed, memo, iterator, callback);
+ };
+
+ async.transform = function (arr, memo, iterator, callback) {
+ if (arguments.length === 3) {
+ callback = iterator;
+ iterator = memo;
+ memo = _isArray(arr) ? [] : {};
+ }
+
+ async.eachOf(arr, function(v, k, cb) {
+ iterator(memo, v, k, cb);
+ }, function(err) {
+ callback(err, memo);
+ });
+ };
+
+ function _filter(eachfn, arr, iterator, callback) {
+ var results = [];
+ eachfn(arr, function (x, index, callback) {
+ iterator(x, function (v) {
+ if (v) {
+ results.push({index: index, value: x});
+ }
+ callback();
+ });
+ }, function () {
+ callback(_map(results.sort(function (a, b) {
+ return a.index - b.index;
+ }), function (x) {
+ return x.value;
+ }));
+ });
+ }
+
+ async.select =
+ async.filter = doParallel(_filter);
+
+ async.selectLimit =
+ async.filterLimit = doParallelLimit(_filter);
+
+ async.selectSeries =
+ async.filterSeries = doSeries(_filter);
+
+ function _reject(eachfn, arr, iterator, callback) {
+ _filter(eachfn, arr, function(value, cb) {
+ iterator(value, function(v) {
+ cb(!v);
+ });
+ }, callback);
+ }
+ async.reject = doParallel(_reject);
+ async.rejectLimit = doParallelLimit(_reject);
+ async.rejectSeries = doSeries(_reject);
+
+ function _createTester(eachfn, check, getResult) {
+ return function(arr, limit, iterator, cb) {
+ function done() {
+ if (cb) cb(getResult(false, void 0));
+ }
+ function iteratee(x, _, callback) {
+ if (!cb) return callback();
+ iterator(x, function (v) {
+ if (cb && check(v)) {
+ cb(getResult(true, x));
+ cb = iterator = false;
+ }
+ callback();
+ });
+ }
+ if (arguments.length > 3) {
+ eachfn(arr, limit, iteratee, done);
+ } else {
+ cb = iterator;
+ iterator = limit;
+ eachfn(arr, iteratee, done);
+ }
+ };
+ }
+
+ async.any =
+ async.some = _createTester(async.eachOf, toBool, identity);
+
+ async.someLimit = _createTester(async.eachOfLimit, toBool, identity);
+
+ async.all =
+ async.every = _createTester(async.eachOf, notId, notId);
+
+ async.everyLimit = _createTester(async.eachOfLimit, notId, notId);
+
+ function _findGetResult(v, x) {
+ return x;
+ }
+ async.detect = _createTester(async.eachOf, identity, _findGetResult);
+ async.detectSeries = _createTester(async.eachOfSeries, identity, _findGetResult);
+ async.detectLimit = _createTester(async.eachOfLimit, identity, _findGetResult);
+
+ async.sortBy = function (arr, iterator, callback) {
+ async.map(arr, function (x, callback) {
+ iterator(x, function (err, criteria) {
+ if (err) {
+ callback(err);
+ }
+ else {
+ callback(null, {value: x, criteria: criteria});
+ }
+ });
+ }, function (err, results) {
+ if (err) {
+ return callback(err);
+ }
+ else {
+ callback(null, _map(results.sort(comparator), function (x) {
+ return x.value;
+ }));
+ }
+
+ });
+
+ function comparator(left, right) {
+ var a = left.criteria, b = right.criteria;
+ return a < b ? -1 : a > b ? 1 : 0;
+ }
+ };
+
+ async.auto = function (tasks, concurrency, callback) {
+ if (typeof arguments[1] === 'function') {
+ // concurrency is optional, shift the args.
+ callback = concurrency;
+ concurrency = null;
+ }
+ callback = _once(callback || noop);
+ var keys = _keys(tasks);
+ var remainingTasks = keys.length;
+ if (!remainingTasks) {
+ return callback(null);
+ }
+ if (!concurrency) {
+ concurrency = remainingTasks;
+ }
+
+ var results = {};
+ var runningTasks = 0;
+
+ var hasError = false;
+
+ var listeners = [];
+ function addListener(fn) {
+ listeners.unshift(fn);
+ }
+ function removeListener(fn) {
+ var idx = _indexOf(listeners, fn);
+ if (idx >= 0) listeners.splice(idx, 1);
+ }
+ function taskComplete() {
+ remainingTasks--;
+ _arrayEach(listeners.slice(0), function (fn) {
+ fn();
+ });
+ }
+
+ addListener(function () {
+ if (!remainingTasks) {
+ callback(null, results);
+ }
+ });
+
+ _arrayEach(keys, function (k) {
+ if (hasError) return;
+ var task = _isArray(tasks[k]) ? tasks[k]: [tasks[k]];
+ var taskCallback = _restParam(function(err, args) {
+ runningTasks--;
+ if (args.length <= 1) {
+ args = args[0];
+ }
+ if (err) {
+ var safeResults = {};
+ _forEachOf(results, function(val, rkey) {
+ safeResults[rkey] = val;
+ });
+ safeResults[k] = args;
+ hasError = true;
+
+ callback(err, safeResults);
+ }
+ else {
+ results[k] = args;
+ async.setImmediate(taskComplete);
+ }
+ });
+ var requires = task.slice(0, 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) && _indexOf(dep, k) >= 0) {
+ throw new Error('Has cyclic dependencies');
+ }
+ }
+ function ready() {
+ return runningTasks < concurrency && _reduce(requires, function (a, x) {
+ return (a && results.hasOwnProperty(x));
+ }, true) && !results.hasOwnProperty(k);
+ }
+ if (ready()) {
+ runningTasks++;
+ task[task.length - 1](taskCallback, results);
+ }
+ else {
+ addListener(listener);
+ }
+ function listener() {
+ if (ready()) {
+ runningTasks++;
+ removeListener(listener);
+ task[task.length - 1](taskCallback, results);
+ }
+ }
+ });
+ };
+
+
+
+ async.retry = function(times, task, callback) {
+ var DEFAULT_TIMES = 5;
+ var DEFAULT_INTERVAL = 0;
+
+ var attempts = [];
+
+ var opts = {
+ times: DEFAULT_TIMES,
+ interval: DEFAULT_INTERVAL
+ };
+
+ function parseTimes(acc, t){
+ if(typeof t === 'number'){
+ acc.times = parseInt(t, 10) || DEFAULT_TIMES;
+ } else if(typeof t === 'object'){
+ acc.times = parseInt(t.times, 10) || DEFAULT_TIMES;
+ acc.interval = parseInt(t.interval, 10) || DEFAULT_INTERVAL;
+ } else {
+ throw new Error('Unsupported argument type for \'times\': ' + typeof t);
+ }
+ }
+
+ var length = arguments.length;
+ if (length < 1 || length > 3) {
+ throw new Error('Invalid arguments - must be either (task), (task, callback), (times, task) or (times, task, callback)');
+ } else if (length <= 2 && typeof times === 'function') {
+ callback = task;
+ task = times;
+ }
+ if (typeof times !== 'function') {
+ parseTimes(opts, times);
+ }
+ opts.callback = callback;
+ opts.task = task;
+
+ function wrappedTask(wrappedCallback, wrappedResults) {
+ function retryAttempt(task, finalAttempt) {
+ return function(seriesCallback) {
+ task(function(err, result){
+ seriesCallback(!err || finalAttempt, {err: err, result: result});
+ }, wrappedResults);
+ };
+ }
+
+ function retryInterval(interval){
+ return function(seriesCallback){
+ setTimeout(function(){
+ seriesCallback(null);
+ }, interval);
+ };
+ }
+
+ while (opts.times) {
+
+ var finalAttempt = !(opts.times-=1);
+ attempts.push(retryAttempt(opts.task, finalAttempt));
+ if(!finalAttempt && opts.interval > 0){
+ attempts.push(retryInterval(opts.interval));
+ }
+ }
+
+ async.series(attempts, function(done, data){
+ data = data[data.length - 1];
+ (wrappedCallback || opts.callback)(data.err, data.result);
+ });
+ }
+
+ // If a callback is passed, run this as a controll flow
+ return opts.callback ? wrappedTask() : wrappedTask;
+ };
+
+ async.waterfall = function (tasks, callback) {
+ callback = _once(callback || noop);
+ if (!_isArray(tasks)) {
+ var err = new Error('First argument to waterfall must be an array of functions');
+ return callback(err);
+ }
+ if (!tasks.length) {
+ return callback();
+ }
+ function wrapIterator(iterator) {
+ return _restParam(function (err, args) {
+ if (err) {
+ callback.apply(null, [err].concat(args));
+ }
+ else {
+ var next = iterator.next();
+ if (next) {
+ args.push(wrapIterator(next));
+ }
+ else {
+ args.push(callback);
+ }
+ ensureAsync(iterator).apply(null, args);
+ }
+ });
+ }
+ wrapIterator(async.iterator(tasks))();
+ };
+
+ function _parallel(eachfn, tasks, callback) {
+ callback = callback || noop;
+ var results = _isArrayLike(tasks) ? [] : {};
+
+ eachfn(tasks, function (task, key, callback) {
+ task(_restParam(function (err, args) {
+ if (args.length <= 1) {
+ args = args[0];
+ }
+ results[key] = args;
+ callback(err);
+ }));
+ }, function (err) {
+ callback(err, results);
+ });
+ }
+
+ async.parallel = function (tasks, callback) {
+ _parallel(async.eachOf, tasks, callback);
+ };
+
+ async.parallelLimit = function(tasks, limit, callback) {
+ _parallel(_eachOfLimit(limit), tasks, callback);
+ };
+
+ async.series = function(tasks, callback) {
+ _parallel(async.eachOfSeries, tasks, callback);
+ };
+
+ async.iterator = function (tasks) {
+ function makeCallback(index) {
+ function fn() {
+ if (tasks.length) {
+ tasks[index].apply(null, arguments);
+ }
+ return fn.next();
+ }
+ fn.next = function () {
+ return (index < tasks.length - 1) ? makeCallback(index + 1): null;
+ };
+ return fn;
+ }
+ return makeCallback(0);
+ };
+
+ async.apply = _restParam(function (fn, args) {
+ return _restParam(function (callArgs) {
+ return fn.apply(
+ null, args.concat(callArgs)
+ );
+ });
+ });
+
+ function _concat(eachfn, arr, fn, callback) {
+ var result = [];
+ eachfn(arr, function (x, index, cb) {
+ fn(x, function (err, y) {
+ result = result.concat(y || []);
+ cb(err);
+ });
+ }, function (err) {
+ callback(err, result);
+ });
+ }
+ async.concat = doParallel(_concat);
+ async.concatSeries = doSeries(_concat);
+
+ async.whilst = function (test, iterator, callback) {
+ callback = callback || noop;
+ if (test()) {
+ var next = _restParam(function(err, args) {
+ if (err) {
+ callback(err);
+ } else if (test.apply(this, args)) {
+ iterator(next);
+ } else {
+ callback.apply(null, [null].concat(args));
+ }
+ });
+ iterator(next);
+ } else {
+ callback(null);
+ }
+ };
+
+ async.doWhilst = function (iterator, test, callback) {
+ var calls = 0;
+ return async.whilst(function() {
+ return ++calls <= 1 || test.apply(this, arguments);
+ }, iterator, callback);
+ };
+
+ async.until = function (test, iterator, callback) {
+ return async.whilst(function() {
+ return !test.apply(this, arguments);
+ }, iterator, callback);
+ };
+
+ async.doUntil = function (iterator, test, callback) {
+ return async.doWhilst(iterator, function() {
+ return !test.apply(this, arguments);
+ }, callback);
+ };
+
+ async.during = function (test, iterator, callback) {
+ callback = callback || noop;
+
+ var next = _restParam(function(err, args) {
+ if (err) {
+ callback(err);
+ } else {
+ args.push(check);
+ test.apply(this, args);
+ }
+ });
+
+ var check = function(err, truth) {
+ if (err) {
+ callback(err);
+ } else if (truth) {
+ iterator(next);
+ } else {
+ callback(null);
+ }
+ };
+
+ test(check);
+ };
+
+ async.doDuring = function (iterator, test, callback) {
+ var calls = 0;
+ async.during(function(next) {
+ if (calls++ < 1) {
+ next(null, true);
+ } else {
+ test.apply(this, arguments);
+ }
+ }, iterator, callback);
+ };
+
+ function _queue(worker, concurrency, payload) {
+ if (concurrency == null) {
+ concurrency = 1;
+ }
+ else if(concurrency === 0) {
+ throw new Error('Concurrency must not be zero');
+ }
+ function _insert(q, data, pos, callback) {
+ if (callback != null && typeof callback !== "function") {
+ throw new Error("task callback must be a function");
+ }
+ q.started = true;
+ if (!_isArray(data)) {
+ data = [data];
+ }
+ if(data.length === 0 && q.idle()) {
+ // call drain immediately if there are no tasks
+ return async.setImmediate(function() {
+ q.drain();
+ });
+ }
+ _arrayEach(data, function(task) {
+ var item = {
+ data: task,
+ callback: callback || noop
+ };
+
+ if (pos) {
+ q.tasks.unshift(item);
+ } else {
+ q.tasks.push(item);
+ }
+
+ if (q.tasks.length === q.concurrency) {
+ q.saturated();
+ }
+ });
+ async.setImmediate(q.process);
+ }
+ function _next(q, tasks) {
+ return function(){
+ workers -= 1;
+
+ var removed = false;
+ var args = arguments;
+ _arrayEach(tasks, function (task) {
+ _arrayEach(workersList, function (worker, index) {
+ if (worker === task && !removed) {
+ workersList.splice(index, 1);
+ removed = true;
+ }
+ });
+
+ task.callback.apply(task, args);
+ });
+ if (q.tasks.length + workers === 0) {
+ q.drain();
+ }
+ q.process();
+ };
+ }
+
+ var workers = 0;
+ var workersList = [];
+ var q = {
+ tasks: [],
+ concurrency: concurrency,
+ payload: payload,
+ saturated: noop,
+ empty: noop,
+ drain: noop,
+ started: false,
+ paused: false,
+ push: function (data, callback) {
+ _insert(q, data, false, callback);
+ },
+ kill: function () {
+ q.drain = noop;
+ q.tasks = [];
+ },
+ unshift: function (data, callback) {
+ _insert(q, data, true, callback);
+ },
+ process: function () {
+ while(!q.paused && workers < q.concurrency && q.tasks.length){
+
+ var tasks = q.payload ?
+ q.tasks.splice(0, q.payload) :
+ q.tasks.splice(0, q.tasks.length);
+
+ var data = _map(tasks, function (task) {
+ return task.data;
+ });
+
+ if (q.tasks.length === 0) {
+ q.empty();
+ }
+ workers += 1;
+ workersList.push(tasks[0]);
+ var cb = only_once(_next(q, tasks));
+ worker(data, cb);
+ }
+ },
+ length: function () {
+ return q.tasks.length;
+ },
+ running: function () {
+ return workers;
+ },
+ workersList: function () {
+ return workersList;
+ },
+ idle: function() {
+ return q.tasks.length + workers === 0;
+ },
+ pause: function () {
+ q.paused = true;
+ },
+ 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 <= resumeCount; w++) {
+ async.setImmediate(q.process);
+ }
+ }
+ };
+ return q;
+ }
+
+ async.queue = function (worker, concurrency) {
+ var q = _queue(function (items, cb) {
+ worker(items[0], cb);
+ }, concurrency, 1);
+
+ return q;
+ };
+
+ async.priorityQueue = function (worker, concurrency) {
+
+ function _compareTasks(a, b){
+ return a.priority - b.priority;
+ }
+
+ function _binarySearch(sequence, item, compare) {
+ var beg = -1,
+ end = sequence.length - 1;
+ while (beg < end) {
+ var mid = beg + ((end - beg + 1) >>> 1);
+ if (compare(item, sequence[mid]) >= 0) {
+ beg = mid;
+ } else {
+ end = mid - 1;
+ }
+ }
+ return beg;
+ }
+
+ function _insert(q, data, priority, callback) {
+ if (callback != null && typeof callback !== "function") {
+ throw new Error("task callback must be a function");
+ }
+ q.started = true;
+ if (!_isArray(data)) {
+ data = [data];
+ }
+ if(data.length === 0) {
+ // call drain immediately if there are no tasks
+ return async.setImmediate(function() {
+ q.drain();
+ });
+ }
+ _arrayEach(data, function(task) {
+ var item = {
+ data: task,
+ priority: priority,
+ callback: typeof callback === 'function' ? callback : noop
+ };
+
+ q.tasks.splice(_binarySearch(q.tasks, item, _compareTasks) + 1, 0, item);
+
+ if (q.tasks.length === q.concurrency) {
+ q.saturated();
+ }
+ async.setImmediate(q.process);
+ });
+ }
+
+ // Start with a normal queue
+ var q = async.queue(worker, concurrency);
+
+ // Override push to accept second parameter representing priority
+ q.push = function (data, priority, callback) {
+ _insert(q, data, priority, callback);
+ };
+
+ // Remove unshift function
+ delete q.unshift;
+
+ return q;
+ };
+
+ async.cargo = function (worker, payload) {
+ return _queue(worker, 1, payload);
+ };
+
+ function _console_fn(name) {
+ return _restParam(function (fn, args) {
+ fn.apply(null, args.concat([_restParam(function (err, args) {
+ if (typeof console === 'object') {
+ if (err) {
+ if (console.error) {
+ console.error(err);
+ }
+ }
+ else if (console[name]) {
+ _arrayEach(args, function (x) {
+ console[name](x);
+ });
+ }
+ }
+ })]));
+ });
+ }
+ async.log = _console_fn('log');
+ async.dir = _console_fn('dir');
+ /*async.info = _console_fn('info');
+ async.warn = _console_fn('warn');
+ async.error = _console_fn('error');*/
+
+ async.memoize = function (fn, hasher) {
+ var memo = {};
+ var queues = {};
+ hasher = hasher || identity;
+ var memoized = _restParam(function memoized(args) {
+ var callback = args.pop();
+ var key = hasher.apply(null, args);
+ if (key in memo) {
+ async.setImmediate(function () {
+ callback.apply(null, memo[key]);
+ });
+ }
+ else if (key in queues) {
+ queues[key].push(callback);
+ }
+ else {
+ queues[key] = [callback];
+ fn.apply(null, args.concat([_restParam(function (args) {
+ memo[key] = args;
+ var q = queues[key];
+ delete queues[key];
+ for (var i = 0, l = q.length; i < l; i++) {
+ q[i].apply(null, args);
+ }
+ })]));
+ }
+ });
+ memoized.memo = memo;
+ memoized.unmemoized = fn;
+ return memoized;
+ };
+
+ async.unmemoize = function (fn) {
+ return function () {
+ return (fn.unmemoized || fn).apply(null, arguments);
+ };
+ };
+
+ function _times(mapper) {
+ return function (count, iterator, callback) {
+ mapper(_range(count), iterator, callback);
+ };
+ }
+
+ async.times = _times(async.map);
+ async.timesSeries = _times(async.mapSeries);
+ async.timesLimit = function (count, limit, iterator, callback) {
+ return async.mapLimit(_range(count), limit, iterator, callback);
+ };
+
+ async.seq = function (/* functions... */) {
+ var fns = arguments;
+ return _restParam(function (args) {
+ var that = this;
+
+ var callback = args[args.length - 1];
+ if (typeof callback == 'function') {
+ args.pop();
+ } else {
+ callback = noop;
+ }
+
+ async.reduce(fns, args, function (newargs, fn, cb) {
+ fn.apply(that, newargs.concat([_restParam(function (err, nextargs) {
+ cb(err, nextargs);
+ })]));
+ },
+ function (err, results) {
+ callback.apply(that, [err].concat(results));
+ });
+ });
+ };
+
+ async.compose = function (/* functions... */) {
+ return async.seq.apply(null, Array.prototype.reverse.call(arguments));
+ };
+
+
+ function _applyEach(eachfn) {
+ return _restParam(function(fns, args) {
+ var go = _restParam(function(args) {
+ var that = this;
+ var callback = args.pop();
+ return eachfn(fns, function (fn, _, cb) {
+ fn.apply(that, args.concat([cb]));
+ },
+ callback);
+ });
+ if (args.length) {
+ return go.apply(this, args);
+ }
+ else {
+ return go;
+ }
+ });
+ }
+
+ async.applyEach = _applyEach(async.eachOf);
+ async.applyEachSeries = _applyEach(async.eachOfSeries);
+
+
+ async.forever = function (fn, callback) {
+ var done = only_once(callback || noop);
+ var task = ensureAsync(fn);
+ function next(err) {
+ if (err) {
+ return done(err);
+ }
+ task(next);
+ }
+ next();
+ };
+
+ function ensureAsync(fn) {
+ return _restParam(function (args) {
+ var callback = args.pop();
+ args.push(function () {
+ var innerArgs = arguments;
+ if (sync) {
+ async.setImmediate(function () {
+ callback.apply(null, innerArgs);
+ });
+ } else {
+ callback.apply(null, innerArgs);
+ }
+ });
+ var sync = true;
+ fn.apply(this, args);
+ sync = false;
+ });
+ }
+
+ async.ensureAsync = ensureAsync;
+
+ async.constant = _restParam(function(values) {
+ var args = [null].concat(values);
+ return function (callback) {
+ return callback.apply(this, args);
+ };
+ });
+
+ async.wrapSync =
+ async.asyncify = function asyncify(func) {
+ return _restParam(function (args) {
+ var callback = args.pop();
+ var result;
+ try {
+ result = func.apply(this, args);
+ } catch (e) {
+ return callback(e);
+ }
+ // if result is Promise object
+ if (_isObject(result) && typeof result.then === "function") {
+ result.then(function(value) {
+ callback(null, value);
+ })["catch"](function(err) {
+ callback(err.message ? err : new Error(err));
+ });
+ } else {
+ callback(null, result);
+ }
+ });
+ };
+
+ // Node.js
+ if (typeof module === 'object' && module.exports) {
+ module.exports = async;
+ }
+ // AMD / RequireJS
+ else if (typeof define === 'function' && define.amd) {
+ define([], function () {
+ return async;
+ });
+ }
+ // included directly via <script> tag
+ else {
+ root.async = async;
+ }
+
+}());
diff --git a/dist/async.min.js b/dist/async.min.js
new file mode 100644
index 0000000..9b0028d
--- /dev/null
+++ b/dist/async.min.js
@@ -0,0 +1,2 @@
+!function(){function n(){}function t(n){return n}function e(n){return!!n}function r(n){return!n}function u(n){return function(){if(null===n)throw new Error("Callback was already called.");n.apply(this,arguments),n=null}}function i(n){return function(){null!==n&&(n.apply(this,arguments),n=null)}}function o(n){return M(n)||"number"==typeof n.length&&n.length>=0&&n.length%1===0}function c(n,t){for(var e=-1,r=n.length;++e<r;)t(n[e],e,n)}function a(n,t){for(var e=-1,r=n.length,u=Array(r);++e<r;)u[e]=t(n[e],e,n);return u}function f(n){return a(Array(n),function(n,t){return t})}function l(n,t,e){return c(n,function(n,r,u){e=t(e,n,r,u)}),e}function s(n,t){c(W(n),function(e){t(n[e],e)})}function p(n,t){for(var e=0;e<n.length;e++)if(n[e]===t)return e;return-1}function h(n){var t,e,r=-1;return o(n)?(t=n.length,function(){return r++,t>r?r:null}):(e=W(n),t=e.length,function(){return r++,t>r?e[r]:null})}function m(n,t){return t=null==t?n.length-1:+t,function(){for(var e=Math.max(arguments.length-t,0),r=Array(e),u=0;e>u;u++)r[u]=arguments[u+t];switch(t){case 0:return n.call(this,r);case 1:return n.call(this,arguments[0],r)}}}function y(n){return function(t,e,r){return n(t,r)}}function v(t){return function(e,r,o){o=i(o||n),e=e||[];var c=h(e);if(0>=t)return o(null);var a=!1,f=0,l=!1;!function s(){if(a&&0>=f)return o(null);for(;t>f&&!l;){var n=c();if(null===n)return a=!0,void(0>=f&&o(null));f+=1,r(e[n],n,u(function(n){f-=1,n?(o(n),l=!0):s()}))}}()}}function d(n){return function(t,e,r){return n(C.eachOf,t,e,r)}}function g(n){return function(t,e,r,u){return n(v(e),t,r,u)}}function k(n){return function(t,e,r){return n(C.eachOfSeries,t,e,r)}}function b(t,e,r,u){u=i(u||n),e=e||[];var c=o(e)?[]:{};t(e,function(n,t,e){r(n,function(n,r){c[t]=r,e(n)})},function(n){u(n,c)})}function w(n,t,e,r){var u=[];n(t,function(n,t,r){e(n,function(e){e&&u.push({index:t,value:n}),r()})},function(){r(a(u.sort(function(n,t){return n.index-t.index}),function(n){return n.value}))})}function O(n,t,e,r){w(n,t,function(n,t){e(n,function(n){t(!n)})},r)}function S(n,t,e){return function(r,u,i,o){function c(){o&&o(e(!1,void 0))}function a(n,r,u){return o?void i(n,function(r){o&&t(r)&&(o(e(!0,n)),o=i=!1),u()}):u()}arguments.length>3?n(r,u,a,c):(o=i,i=u,n(r,a,c))}}function E(n,t){return t}function L(t,e,r){r=r||n;var u=o(e)?[]:{};t(e,function(n,t,e){n(m(function(n,r){r.length<=1&&(r=r[0]),u[t]=r,e(n)}))},function(n){r(n,u)})}function I(n,t,e,r){var u=[];n(t,function(n,t,r){e(n,function(n,t){u=u.concat(t||[]),r(n)})},function(n){r(n,u)})}function x(t,e,r){function i(t,e,r,u){if(null!=u&&"function"!=typeof u)throw new Error("task callback must be a function");return t.started=!0,M(e)||(e=[e]),0===e.length&&t.idle()?C.setImmediate(function(){t.drain()}):(c(e,function(e){var i={data:e,callback:u||n};r?t.tasks.unshift(i):t.tasks.push(i),t.tasks.length===t.concurrency&&t.saturated()}),void C.setImmediate(t.process))}function o(n,t){return function(){f-=1;var e=!1,r=arguments;c(t,function(n){c(l,function(t,r){t!==n||e||(l.splice(r,1),e=!0)}),n.callback.apply(n,r)}),n.tasks.length+f===0&&n.drain(),n.process()}}if(null==e)e=1;else if(0===e)throw new Error("Concurrency must not be zero");var f=0,l=[],s={tasks:[],concurrency:e,payload:r,saturated:n,empty:n,drain:n,started:!1,paused:!1,push:function(n,t){i(s,n,!1,t)},kill:function(){s.drain=n,s.tasks=[]},unshift:function(n,t){i(s,n,!0,t)},process:function(){for(;!s.paused&&f<s.concurrency&&s.tasks.length;){var n=s.payload?s.tasks.splice(0,s.payload):s.tasks.splice(0,s.tasks.length),e=a(n,function(n){return n.data});0===s.tasks.length&&s.empty(),f+=1,l.push(n[0]);var r=u(o(s,n));t(e,r)}},length:function(){return s.tasks.length},running:function(){return f},workersList:function(){return l},idle:function(){return s.tasks.length+f===0},pause:function(){s.paused=!0},resume:function(){if(s.paused!==!1){s.paused=!1;for(var n=Math.min(s.concurrency,s.tasks.length),t=1;n>=t;t++)C.setImmediate(s.process)}}};return s}function j(n){return m(function(t,e){t.apply(null,e.concat([m(function(t,e){"object"==typeof console&&(t?console.error&&console.error(t):console[n]&&c(e,function(t){console[n](t)}))})]))})}function A(n){return function(t,e,r){n(f(t),e,r)}}function T(n){return m(function(t,e){var r=m(function(e){var r=this,u=e.pop();return n(t,function(n,t,u){n.apply(r,e.concat([u]))},u)});return e.length?r.apply(this,e):r})}function z(n){return m(function(t){var e=t.pop();t.push(function(){var n=arguments;r?C.setImmediate(function(){e.apply(null,n)}):e.apply(null,n)});var r=!0;n.apply(this,t),r=!1})}var q,C={},P="object"==typeof self&&self.self===self&&self||"object"==typeof global&&global.global===global&&global||this;null!=P&&(q=P.async),C.noConflict=function(){return P.async=q,C};var H=Object.prototype.toString,M=Array.isArray||function(n){return"[object Array]"===H.call(n)},U=function(n){var t=typeof n;return"function"===t||"object"===t&&!!n},W=Object.keys||function(n){var t=[];for(var e in n)n.hasOwnProperty(e)&&t.push(e);return t},B="function"==typeof setImmediate&&setImmediate,D=B?function(n){B(n)}:function(n){setTimeout(n,0)};"object"==typeof process&&"function"==typeof process.nextTick?C.nextTick=process.nextTick:C.nextTick=D,C.setImmediate=B?D:C.nextTick,C.forEach=C.each=function(n,t,e){return C.eachOf(n,y(t),e)},C.forEachSeries=C.eachSeries=function(n,t,e){return C.eachOfSeries(n,y(t),e)},C.forEachLimit=C.eachLimit=function(n,t,e,r){return v(t)(n,y(e),r)},C.forEachOf=C.eachOf=function(t,e,r){function o(n){f--,n?r(n):null===c&&0>=f&&r(null)}r=i(r||n),t=t||[];for(var c,a=h(t),f=0;null!=(c=a());)f+=1,e(t[c],c,u(o));0===f&&r(null)},C.forEachOfSeries=C.eachOfSeries=function(t,e,r){function o(){var n=!0;return null===a?r(null):(e(t[a],a,u(function(t){if(t)r(t);else{if(a=c(),null===a)return r(null);n?C.setImmediate(o):o()}})),void(n=!1))}r=i(r||n),t=t||[];var c=h(t),a=c();o()},C.forEachOfLimit=C.eachOfLimit=function(n,t,e,r){v(t)(n,e,r)},C.map=d(b),C.mapSeries=k(b),C.mapLimit=g(b),C.inject=C.foldl=C.reduce=function(n,t,e,r){C.eachOfSeries(n,function(n,r,u){e(t,n,function(n,e){t=e,u(n)})},function(n){r(n,t)})},C.foldr=C.reduceRight=function(n,e,r,u){var i=a(n,t).reverse();C.reduce(i,e,r,u)},C.transform=function(n,t,e,r){3===arguments.length&&(r=e,e=t,t=M(n)?[]:{}),C.eachOf(n,function(n,r,u){e(t,n,r,u)},function(n){r(n,t)})},C.select=C.filter=d(w),C.selectLimit=C.filterLimit=g(w),C.selectSeries=C.filterSeries=k(w),C.reject=d(O),C.rejectLimit=g(O),C.rejectSeries=k(O),C.any=C.some=S(C.eachOf,e,t),C.someLimit=S(C.eachOfLimit,e,t),C.all=C.every=S(C.eachOf,r,r),C.everyLimit=S(C.eachOfLimit,r,r),C.detect=S(C.eachOf,t,E),C.detectSeries=S(C.eachOfSeries,t,E),C.detectLimit=S(C.eachOfLimit,t,E),C.sortBy=function(n,t,e){function r(n,t){var e=n.criteria,r=t.criteria;return r>e?-1:e>r?1:0}C.map(n,function(n,e){t(n,function(t,r){t?e(t):e(null,{value:n,criteria:r})})},function(n,t){return n?e(n):void e(null,a(t.sort(r),function(n){return n.value}))})},C.auto=function(t,e,r){function u(n){g.unshift(n)}function o(n){var t=p(g,n);t>=0&&g.splice(t,1)}function a(){h--,c(g.slice(0),function(n){n()})}"function"==typeof arguments[1]&&(r=e,e=null),r=i(r||n);var f=W(t),h=f.length;if(!h)return r(null);e||(e=h);var y={},v=0,d=!1,g=[];u(function(){h||r(null,y)}),c(f,function(n){function i(){return e>v&&l(k,function(n,t){return n&&y.hasOwnProperty(t)},!0)&&!y.hasOwnProperty(n)}function c(){i()&&(v++,o(c),h[h.length-1](g,y))}if(!d){for(var f,h=M(t[n])?t[n]:[t[n]],g=m(function(t,e){if(v--,e.length<=1&&(e=e[0]),t){var u={};s(y,function(n,t){u[t]=n}),u[n]=e,d=!0,r(t,u)}else y[n]=e,C.setImmediate(a)}),k=h.slice(0,h.length-1),b=k.length;b--;){if(!(f=t[k[b]]))throw new Error("Has inexistant dependency");if(M(f)&&p(f,n)>=0)throw new Error("Has cyclic dependencies")}i()?(v++,h[h.length-1](g,y)):u(c)}})},C.retry=function(n,t,e){function r(n,t){if("number"==typeof t)n.times=parseInt(t,10)||i;else{if("object"!=typeof t)throw new Error("Unsupported argument type for 'times': "+typeof t);n.times=parseInt(t.times,10)||i,n.interval=parseInt(t.interval,10)||o}}function u(n,t){function e(n,e){return function(r){n(function(n,t){r(!n||e,{err:n,result:t})},t)}}function r(n){return function(t){setTimeout(function(){t(null)},n)}}for(;a.times;){var u=!(a.times-=1);c.push(e(a.task,u)),!u&&a.interval>0&&c.push(r(a.interval))}C.series(c,function(t,e){e=e[e.length-1],(n||a.callback)(e.err,e.result)})}var i=5,o=0,c=[],a={times:i,interval:o},f=arguments.length;if(1>f||f>3)throw new Error("Invalid arguments - must be either (task), (task, callback), (times, task) or (times, task, callback)");return 2>=f&&"function"==typeof n&&(e=t,t=n),"function"!=typeof n&&r(a,n),a.callback=e,a.task=t,a.callback?u():u},C.waterfall=function(t,e){function r(n){return m(function(t,u){if(t)e.apply(null,[t].concat(u));else{var i=n.next();i?u.push(r(i)):u.push(e),z(n).apply(null,u)}})}if(e=i(e||n),!M(t)){var u=new Error("First argument to waterfall must be an array of functions");return e(u)}return t.length?void r(C.iterator(t))():e()},C.parallel=function(n,t){L(C.eachOf,n,t)},C.parallelLimit=function(n,t,e){L(v(t),n,e)},C.series=function(n,t){L(C.eachOfSeries,n,t)},C.iterator=function(n){function t(e){function r(){return n.length&&n[e].apply(null,arguments),r.next()}return r.next=function(){return e<n.length-1?t(e+1):null},r}return t(0)},C.apply=m(function(n,t){return m(function(e){return n.apply(null,t.concat(e))})}),C.concat=d(I),C.concatSeries=k(I),C.whilst=function(t,e,r){if(r=r||n,t()){var u=m(function(n,i){n?r(n):t.apply(this,i)?e(u):r.apply(null,[null].concat(i))});e(u)}else r(null)},C.doWhilst=function(n,t,e){var r=0;return C.whilst(function(){return++r<=1||t.apply(this,arguments)},n,e)},C.until=function(n,t,e){return C.whilst(function(){return!n.apply(this,arguments)},t,e)},C.doUntil=function(n,t,e){return C.doWhilst(n,function(){return!t.apply(this,arguments)},e)},C.during=function(t,e,r){r=r||n;var u=m(function(n,e){n?r(n):(e.push(i),t.apply(this,e))}),i=function(n,t){n?r(n):t?e(u):r(null)};t(i)},C.doDuring=function(n,t,e){var r=0;C.during(function(n){r++<1?n(null,!0):t.apply(this,arguments)},n,e)},C.queue=function(n,t){var e=x(function(t,e){n(t[0],e)},t,1);return e},C.priorityQueue=function(t,e){function r(n,t){return n.priority-t.priority}function u(n,t,e){for(var r=-1,u=n.length-1;u>r;){var i=r+(u-r+1>>>1);e(t,n[i])>=0?r=i:u=i-1}return r}function i(t,e,i,o){if(null!=o&&"function"!=typeof o)throw new Error("task callback must be a function");return t.started=!0,M(e)||(e=[e]),0===e.length?C.setImmediate(function(){t.drain()}):void c(e,function(e){var c={data:e,priority:i,callback:"function"==typeof o?o:n};t.tasks.splice(u(t.tasks,c,r)+1,0,c),t.tasks.length===t.concurrency&&t.saturated(),C.setImmediate(t.process)})}var o=C.queue(t,e);return o.push=function(n,t,e){i(o,n,t,e)},delete o.unshift,o},C.cargo=function(n,t){return x(n,1,t)},C.log=j("log"),C.dir=j("dir"),C.memoize=function(n,e){var r={},u={};e=e||t;var i=m(function(t){var i=t.pop(),o=e.apply(null,t);o in r?C.setImmediate(function(){i.apply(null,r[o])}):o in u?u[o].push(i):(u[o]=[i],n.apply(null,t.concat([m(function(n){r[o]=n;var t=u[o];delete u[o];for(var e=0,i=t.length;i>e;e++)t[e].apply(null,n)})])))});return i.memo=r,i.unmemoized=n,i},C.unmemoize=function(n){return function(){return(n.unmemoized||n).apply(null,arguments)}},C.times=A(C.map),C.timesSeries=A(C.mapSeries),C.timesLimit=function(n,t,e,r){return C.mapLimit(f(n),t,e,r)},C.seq=function(){var t=arguments;return m(function(e){var r=this,u=e[e.length-1];"function"==typeof u?e.pop():u=n,C.reduce(t,e,function(n,t,e){t.apply(r,n.concat([m(function(n,t){e(n,t)})]))},function(n,t){u.apply(r,[n].concat(t))})})},C.compose=function(){return C.seq.apply(null,Array.prototype.reverse.call(arguments))},C.applyEach=T(C.eachOf),C.applyEachSeries=T(C.eachOfSeries),C.forever=function(t,e){function r(n){return n?i(n):void o(r)}var i=u(e||n),o=z(t);r()},C.ensureAsync=z,C.constant=m(function(n){var t=[null].concat(n);return function(n){return n.apply(this,t)}}),C.wrapSync=C.asyncify=function(n){return m(function(t){var e,r=t.pop();try{e=n.apply(this,t)}catch(u){return r(u)}U(e)&&"function"==typeof e.then?e.then(function(n){r(null,n)})["catch"](function(n){r(n.message?n:new Error(n))}):r(null,e)})},"object"==typeof module&&module.exports?module.exports=C:"function"==typeof define&&define.amd?define([],function(){return C}):P.async=C}();
+//# sourceMappingURL=dist/async.min.map \ No newline at end of file
diff --git a/dist/async.min.map b/dist/async.min.map
new file mode 100644
index 0000000..90771f3
--- /dev/null
+++ b/dist/async.min.map
@@ -0,0 +1 @@
+{"version":3,"file":"dist/async.min.js","sources":["dist/async.js"],"names":["noop","identity","v","toBool","notId","only_once","fn","Error","apply","this","arguments","_once","_isArrayLike","arr","_isArray","length","_arrayEach","iterator","index","_map","result","Array","_range","count","i","_reduce","memo","x","a","_forEachOf","object","_keys","key","_indexOf","item","_keyIterator","coll","len","keys","_restParam","func","startIndex","Math","max","rest","call","_withoutIndex","value","callback","_eachOfLimit","limit","obj","nextKey","done","running","errored","replenish","err","doParallel","async","eachOf","doParallelLimit","doSeries","eachOfSeries","_asyncMap","eachfn","results","_filter","push","sort","b","_reject","cb","_createTester","check","getResult","iteratee","_","_findGetResult","_parallel","tasks","task","args","_concat","y","concat","_queue","worker","concurrency","payload","_insert","q","data","pos","started","idle","setImmediate","drain","unshift","saturated","process","_next","workers","removed","workersList","splice","empty","paused","kill","pause","resume","resumeCount","min","w","_console_fn","name","console","error","_times","mapper","_applyEach","fns","go","that","pop","ensureAsync","innerArgs","sync","previous_async","root","self","global","noConflict","_toString","Object","prototype","toString","isArray","_isObject","type","k","hasOwnProperty","_setImmediate","_delay","setTimeout","nextTick","forEach","each","forEachSeries","eachSeries","forEachLimit","eachLimit","forEachOf","completed","iter","forEachOfSeries","iterate","forEachOfLimit","eachOfLimit","map","mapSeries","mapLimit","inject","foldl","reduce","foldr","reduceRight","reversed","reverse","transform","select","filter","selectLimit","filterLimit","selectSeries","filterSeries","reject","rejectLimit","rejectSeries","any","some","someLimit","all","every","everyLimit","detect","detectSeries","detectLimit","sortBy","comparator","left","right","criteria","auto","addListener","listeners","removeListener","idx","taskComplete","remainingTasks","slice","runningTasks","hasError","ready","requires","listener","taskCallback","dep","safeResults","val","rkey","retry","times","parseTimes","acc","t","parseInt","DEFAULT_TIMES","interval","DEFAULT_INTERVAL","wrappedTask","wrappedCallback","wrappedResults","retryAttempt","finalAttempt","seriesCallback","retryInterval","opts","attempts","series","waterfall","wrapIterator","next","parallel","parallelLimit","makeCallback","callArgs","concatSeries","whilst","test","doWhilst","calls","until","doUntil","during","truth","doDuring","queue","items","priorityQueue","_compareTasks","priority","_binarySearch","sequence","compare","beg","end","mid","cargo","log","dir","memoize","hasher","queues","memoized","l","unmemoized","unmemoize","timesSeries","timesLimit","seq","newargs","nextargs","compose","applyEach","applyEachSeries","forever","constant","values","wrapSync","asyncify","e","then","message","module","exports","define","amd"],"mappings":"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"} \ No newline at end of file
diff --git a/karma.conf.js b/karma.conf.js
new file mode 100644
index 0000000..9e048c3
--- /dev/null
+++ b/karma.conf.js
@@ -0,0 +1,12 @@
+module.exports = function (config) {
+ config.set({
+ browsers: ['Firefox'],
+ files: ['mocha_test/*.js'],
+ frameworks: ['browserify', 'mocha'],
+ preprocessors: {
+ 'mocha_test/*.js': ['browserify']
+ },
+ reporters: ['mocha'],
+ singleRun: true
+ });
+}
diff --git a/lib/async.js b/lib/async.js
index 4d2d69f..e38c901 100755
--- a/lib/async.js
+++ b/lib/async.js
@@ -5,18 +5,32 @@
* 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 = {};
+ function noop() {}
+ function identity(v) {
+ return v;
+ }
+ function toBool(v) {
+ return !!v;
+ }
+ function notId(v) {
+ return !v;
+ }
// global on the server, window in the browser
- var root, previous_async;
+ var previous_async;
+
+ // Establish the root object, `window` (`self`) in the browser, `global`
+ // on the server, or `this` in some virtual machines. We use `self`
+ // instead of `window` for `WebWorker` support.
+ var root = typeof self === 'object' && self.self === self && self ||
+ typeof global === 'object' && global.global === global && global ||
+ this;
- root = this;
if (root != null) {
- previous_async = root.async;
+ previous_async = root.async;
}
async.noConflict = function () {
@@ -25,12 +39,19 @@
};
function only_once(fn) {
- var called = false;
return function() {
- if (called) throw new Error("Callback was already called.");
- called = true;
- fn.apply(root, arguments);
- }
+ if (fn === null) throw new Error("Callback was already called.");
+ fn.apply(this, arguments);
+ fn = null;
+ };
+ }
+
+ function _once(fn) {
+ return function() {
+ if (fn === null) return;
+ fn.apply(this, arguments);
+ fn = null;
+ };
}
//// cross-browser compatiblity functions ////
@@ -41,40 +62,66 @@
return _toString.call(obj) === '[object Array]';
};
- 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);
- }
+ // Ported from underscore.js isObject
+ var _isObject = function(obj) {
+ var type = typeof obj;
+ return type === 'function' || type === 'object' && !!obj;
};
- var _map = function (arr, iterator) {
- if (arr.map) {
- return arr.map(iterator);
+ function _isArrayLike(arr) {
+ return _isArray(arr) || (
+ // has a positive integer length property
+ typeof arr.length === "number" &&
+ arr.length >= 0 &&
+ arr.length % 1 === 0
+ );
+ }
+
+ function _arrayEach(arr, iterator) {
+ var index = -1,
+ length = arr.length;
+
+ while (++index < length) {
+ iterator(arr[index], index, arr);
}
- var results = [];
- _each(arr, function (x, i, a) {
- results.push(iterator(x, i, a));
- });
- return results;
- };
+ }
+
+ function _map(arr, iterator) {
+ var index = -1,
+ length = arr.length,
+ result = Array(length);
- var _reduce = function (arr, iterator, memo) {
- if (arr.reduce) {
- return arr.reduce(iterator, memo);
+ while (++index < length) {
+ result[index] = iterator(arr[index], index, arr);
}
- _each(arr, function (x, i, a) {
+ return result;
+ }
+
+ function _range(count) {
+ return _map(Array(count), function (v, i) { return i; });
+ }
+
+ function _reduce(arr, iterator, memo) {
+ _arrayEach(arr, function (x, i, a) {
memo = iterator(memo, x, i, a);
});
return memo;
- };
+ }
+
+ function _forEachOf(object, iterator) {
+ _arrayEach(_keys(object), function (key) {
+ iterator(object[key], key);
+ });
+ }
- var _keys = function (obj) {
- if (Object.keys) {
- return Object.keys(obj);
+ function _indexOf(arr, item) {
+ for (var i = 0; i < arr.length; i++) {
+ if (arr[i] === item) return i;
}
+ return -1;
+ }
+
+ var _keys = Object.keys || function (obj) {
var keys = [];
for (var k in obj) {
if (obj.hasOwnProperty(k)) {
@@ -84,191 +131,247 @@
return keys;
};
- //// exported async module functions ////
-
- //// nextTick implementation with browser-compatible fallback ////
- if (typeof process === 'undefined' || !(process.nextTick)) {
- if (typeof setImmediate === 'function') {
- async.nextTick = function (fn) {
- // not a direct alias for IE10 compatibility
- setImmediate(fn);
+ function _keyIterator(coll) {
+ var i = -1;
+ var len;
+ var keys;
+ if (_isArrayLike(coll)) {
+ len = coll.length;
+ return function next() {
+ i++;
+ return i < len ? i : null;
};
- async.setImmediate = async.nextTick;
- }
- else {
- async.nextTick = function (fn) {
- setTimeout(fn, 0);
+ } else {
+ keys = _keys(coll);
+ len = keys.length;
+ return function next() {
+ i++;
+ return i < len ? keys[i] : null;
};
- async.setImmediate = async.nextTick;
}
}
- else {
+
+ // Similar to ES6's rest param (http://ariya.ofilabs.com/2013/03/es6-and-rest-parameter.html)
+ // This accumulates the arguments passed into an array, after a given index.
+ // From underscore.js (https://github.com/jashkenas/underscore/pull/2140).
+ function _restParam(func, startIndex) {
+ startIndex = startIndex == null ? func.length - 1 : +startIndex;
+ return function() {
+ var length = Math.max(arguments.length - startIndex, 0);
+ var rest = Array(length);
+ for (var index = 0; index < length; index++) {
+ rest[index] = arguments[index + startIndex];
+ }
+ switch (startIndex) {
+ case 0: return func.call(this, rest);
+ case 1: return func.call(this, arguments[0], rest);
+ }
+ // Currently unused but handle cases outside of the switch statement:
+ // var args = Array(startIndex + 1);
+ // for (index = 0; index < startIndex; index++) {
+ // args[index] = arguments[index];
+ // }
+ // args[startIndex] = rest;
+ // return func.apply(this, args);
+ };
+ }
+
+ function _withoutIndex(iterator) {
+ return function (value, index, callback) {
+ return iterator(value, callback);
+ };
+ }
+
+ //// exported async module functions ////
+
+ //// nextTick implementation with browser-compatible fallback ////
+
+ // capture the global reference to guard against fakeTimer mocks
+ var _setImmediate = typeof setImmediate === 'function' && setImmediate;
+
+ var _delay = _setImmediate ? function(fn) {
+ // not a direct alias for IE10 compatibility
+ _setImmediate(fn);
+ } : function(fn) {
+ setTimeout(fn, 0);
+ };
+
+ if (typeof process === 'object' && typeof process.nextTick === 'function') {
async.nextTick = process.nextTick;
- if (typeof setImmediate !== 'undefined') {
- async.setImmediate = function (fn) {
- // not a direct alias for IE10 compatibility
- setImmediate(fn);
- };
- }
- else {
- async.setImmediate = async.nextTick;
- }
+ } else {
+ async.nextTick = _delay;
}
+ async.setImmediate = _setImmediate ? _delay : async.nextTick;
+
+ async.forEach =
async.each = function (arr, iterator, callback) {
- callback = callback || function () {};
- if (!arr.length) {
- return callback();
+ return async.eachOf(arr, _withoutIndex(iterator), callback);
+ };
+
+ async.forEachSeries =
+ async.eachSeries = function (arr, iterator, callback) {
+ return async.eachOfSeries(arr, _withoutIndex(iterator), callback);
+ };
+
+
+ async.forEachLimit =
+ async.eachLimit = function (arr, limit, iterator, callback) {
+ return _eachOfLimit(limit)(arr, _withoutIndex(iterator), callback);
+ };
+
+ async.forEachOf =
+ async.eachOf = function (object, iterator, callback) {
+ callback = _once(callback || noop);
+ object = object || [];
+
+ var iter = _keyIterator(object);
+ var key, completed = 0;
+
+ while ((key = iter()) != null) {
+ completed += 1;
+ iterator(object[key], key, only_once(done));
}
- var completed = 0;
- _each(arr, function (x) {
- iterator(x, only_once(done) );
- });
+
+ if (completed === 0) callback(null);
+
function done(err) {
- if (err) {
- callback(err);
- callback = function () {};
- }
- else {
- completed += 1;
- if (completed >= arr.length) {
- callback();
- }
- }
+ completed--;
+ if (err) {
+ callback(err);
+ }
+ // Check key is null in case iterator isn't exhausted
+ // and done resolved synchronously.
+ else if (key === null && completed <= 0) {
+ callback(null);
+ }
}
};
- async.forEach = async.each;
- async.eachSeries = function (arr, iterator, callback) {
- callback = callback || function () {};
- if (!arr.length) {
- return callback();
- }
- var completed = 0;
- var iterate = function () {
- iterator(arr[completed], function (err) {
+ async.forEachOfSeries =
+ async.eachOfSeries = function (obj, iterator, callback) {
+ callback = _once(callback || noop);
+ obj = obj || [];
+ var nextKey = _keyIterator(obj);
+ var key = nextKey();
+ function iterate() {
+ var sync = true;
+ if (key === null) {
+ return callback(null);
+ }
+ iterator(obj[key], key, only_once(function (err) {
if (err) {
callback(err);
- callback = function () {};
}
else {
- completed += 1;
- if (completed >= arr.length) {
- callback();
- }
- else {
- iterate();
+ key = nextKey();
+ if (key === null) {
+ return callback(null);
+ } else {
+ if (sync) {
+ async.setImmediate(iterate);
+ } else {
+ iterate();
+ }
}
}
- });
- };
+ }));
+ sync = false;
+ }
iterate();
};
- async.forEachSeries = async.eachSeries;
- async.eachLimit = function (arr, limit, iterator, callback) {
- var fn = _eachLimit(limit);
- fn.apply(null, [arr, iterator, callback]);
+
+
+ async.forEachOfLimit =
+ async.eachOfLimit = function (obj, limit, iterator, callback) {
+ _eachOfLimit(limit)(obj, iterator, callback);
};
- async.forEachLimit = async.eachLimit;
- var _eachLimit = function (limit) {
+ function _eachOfLimit(limit) {
- return function (arr, iterator, callback) {
- callback = callback || function () {};
- if (!arr.length || limit <= 0) {
- return callback();
+ return function (obj, iterator, callback) {
+ callback = _once(callback || noop);
+ obj = obj || [];
+ var nextKey = _keyIterator(obj);
+ if (limit <= 0) {
+ return callback(null);
}
- var completed = 0;
- var started = 0;
+ var done = false;
var running = 0;
+ var errored = false;
(function replenish () {
- if (completed >= arr.length) {
- return callback();
+ if (done && running <= 0) {
+ return callback(null);
}
- while (running < limit && started < arr.length) {
- started += 1;
+ while (running < limit && !errored) {
+ var key = nextKey();
+ if (key === null) {
+ done = true;
+ if (running <= 0) {
+ callback(null);
+ }
+ return;
+ }
running += 1;
- iterator(arr[started - 1], function (err) {
+ iterator(obj[key], key, only_once(function (err) {
+ running -= 1;
if (err) {
callback(err);
- callback = function () {};
+ errored = true;
}
else {
- completed += 1;
- running -= 1;
- if (completed >= arr.length) {
- callback();
- }
- else {
- replenish();
- }
+ replenish();
}
- });
+ }));
}
})();
};
- };
+ }
- var doParallel = function (fn) {
- return function () {
- var args = Array.prototype.slice.call(arguments);
- return fn.apply(null, [async.each].concat(args));
+ function doParallel(fn) {
+ return function (obj, iterator, callback) {
+ return fn(async.eachOf, obj, iterator, callback);
};
- };
- var doParallelLimit = function(limit, fn) {
- return function () {
- var args = Array.prototype.slice.call(arguments);
- return fn.apply(null, [_eachLimit(limit)].concat(args));
+ }
+ function doParallelLimit(fn) {
+ return function (obj, limit, iterator, callback) {
+ return fn(_eachOfLimit(limit), obj, iterator, callback);
};
- };
- var doSeries = function (fn) {
- return function () {
- var args = Array.prototype.slice.call(arguments);
- return fn.apply(null, [async.eachSeries].concat(args));
+ }
+ function doSeries(fn) {
+ return function (obj, iterator, callback) {
+ return fn(async.eachOfSeries, obj, iterator, callback);
};
- };
-
+ }
- var _asyncMap = function (eachfn, arr, iterator, callback) {
- arr = _map(arr, function (x, i) {
- return {index: i, value: x};
- });
- if (!callback) {
- eachfn(arr, function (x, callback) {
- iterator(x.value, function (err) {
- callback(err);
- });
- });
- } else {
- var results = [];
- eachfn(arr, function (x, callback) {
- iterator(x.value, function (err, v) {
- results[x.index] = v;
- callback(err);
- });
- }, function (err) {
- callback(err, results);
+ function _asyncMap(eachfn, arr, iterator, callback) {
+ callback = _once(callback || noop);
+ arr = arr || [];
+ var results = _isArrayLike(arr) ? [] : {};
+ eachfn(arr, function (value, index, callback) {
+ iterator(value, function (err, v) {
+ results[index] = v;
+ callback(err);
});
- }
- };
+ }, function (err) {
+ callback(err, results);
+ });
+ }
+
async.map = doParallel(_asyncMap);
async.mapSeries = doSeries(_asyncMap);
- async.mapLimit = function (arr, limit, iterator, callback) {
- return _mapLimit(limit)(arr, iterator, callback);
- };
-
- var _mapLimit = function(limit) {
- return doParallelLimit(limit, _asyncMap);
- };
+ async.mapLimit = doParallelLimit(_asyncMap);
// reduce only has a series version, as doing reduce in parallel won't
// work in many situations.
+ async.inject =
+ async.foldl =
async.reduce = function (arr, memo, iterator, callback) {
- async.eachSeries(arr, function (x, callback) {
+ async.eachOfSeries(arr, function (x, i, callback) {
iterator(memo, x, function (err, v) {
memo = v;
callback(err);
@@ -277,118 +380,106 @@
callback(err, memo);
});
};
- // inject alias
- async.inject = async.reduce;
- // foldl alias
- async.foldl = async.reduce;
+ async.foldr =
async.reduceRight = function (arr, memo, iterator, callback) {
- var reversed = _map(arr, function (x) {
- return x;
- }).reverse();
+ var reversed = _map(arr, identity).reverse();
async.reduce(reversed, memo, iterator, callback);
};
- // foldr alias
- async.foldr = async.reduceRight;
- var _filter = function (eachfn, arr, iterator, callback) {
- var results = [];
- arr = _map(arr, function (x, i) {
- return {index: i, value: x};
+ async.transform = function (arr, memo, iterator, callback) {
+ if (arguments.length === 3) {
+ callback = iterator;
+ iterator = memo;
+ memo = _isArray(arr) ? [] : {};
+ }
+
+ async.eachOf(arr, function(v, k, cb) {
+ iterator(memo, v, k, cb);
+ }, function(err) {
+ callback(err, memo);
});
- eachfn(arr, function (x, callback) {
- iterator(x.value, function (v) {
+ };
+
+ function _filter(eachfn, arr, iterator, callback) {
+ var results = [];
+ eachfn(arr, function (x, index, callback) {
+ iterator(x, function (v) {
if (v) {
- results.push(x);
+ results.push({index: index, value: x});
}
callback();
});
- }, function (err) {
+ }, function () {
callback(_map(results.sort(function (a, b) {
return a.index - b.index;
}), function (x) {
return x.value;
}));
});
- };
+ }
+
+ async.select =
async.filter = doParallel(_filter);
+
+ async.selectLimit =
+ async.filterLimit = doParallelLimit(_filter);
+
+ async.selectSeries =
async.filterSeries = doSeries(_filter);
- // select alias
- async.select = async.filter;
- async.selectSeries = async.filterSeries;
- var _reject = function (eachfn, arr, iterator, callback) {
- var results = [];
- arr = _map(arr, function (x, i) {
- return {index: i, value: x};
- });
- eachfn(arr, function (x, callback) {
- iterator(x.value, function (v) {
- if (!v) {
- results.push(x);
- }
- callback();
+ function _reject(eachfn, arr, iterator, callback) {
+ _filter(eachfn, arr, function(value, cb) {
+ iterator(value, function(v) {
+ cb(!v);
});
- }, function (err) {
- callback(_map(results.sort(function (a, b) {
- return a.index - b.index;
- }), function (x) {
- return x.value;
- }));
- });
- };
+ }, callback);
+ }
async.reject = doParallel(_reject);
+ async.rejectLimit = doParallelLimit(_reject);
async.rejectSeries = doSeries(_reject);
- var _detect = function (eachfn, arr, iterator, main_callback) {
- eachfn(arr, function (x, callback) {
- iterator(x, function (result) {
- if (result) {
- main_callback(x);
- main_callback = function () {};
- }
- else {
+ function _createTester(eachfn, check, getResult) {
+ return function(arr, limit, iterator, cb) {
+ function done() {
+ if (cb) cb(getResult(false, void 0));
+ }
+ function iteratee(x, _, callback) {
+ if (!cb) return callback();
+ iterator(x, function (v) {
+ if (cb && check(v)) {
+ cb(getResult(true, x));
+ cb = iterator = false;
+ }
callback();
- }
- });
- }, function (err) {
- main_callback();
- });
- };
- async.detect = doParallel(_detect);
- async.detectSeries = doSeries(_detect);
+ });
+ }
+ if (arguments.length > 3) {
+ eachfn(arr, limit, iteratee, done);
+ } else {
+ cb = iterator;
+ iterator = limit;
+ eachfn(arr, iteratee, done);
+ }
+ };
+ }
- async.some = function (arr, iterator, main_callback) {
- async.each(arr, function (x, callback) {
- iterator(x, function (v) {
- if (v) {
- main_callback(true);
- main_callback = function () {};
- }
- callback();
- });
- }, function (err) {
- main_callback(false);
- });
- };
- // any alias
- async.any = async.some;
+ async.any =
+ async.some = _createTester(async.eachOf, toBool, identity);
- async.every = function (arr, iterator, main_callback) {
- async.each(arr, function (x, callback) {
- iterator(x, function (v) {
- if (!v) {
- main_callback(false);
- main_callback = function () {};
- }
- callback();
- });
- }, function (err) {
- main_callback(true);
- });
- };
- // all alias
- async.all = async.every;
+ async.someLimit = _createTester(async.eachOfLimit, toBool, identity);
+
+ async.all =
+ async.every = _createTester(async.eachOf, notId, notId);
+
+ async.everyLimit = _createTester(async.eachOfLimit, notId, notId);
+
+ function _findGetResult(v, x) {
+ return x;
+ }
+ async.detect = _createTester(async.eachOf, identity, _findGetResult);
+ async.detectSeries = _createTester(async.eachOfSeries, identity, _findGetResult);
+ async.detectLimit = _createTester(async.eachOfLimit, identity, _findGetResult);
async.sortBy = function (arr, iterator, callback) {
async.map(arr, function (x, callback) {
@@ -405,181 +496,238 @@
return callback(err);
}
else {
- var fn = function (left, right) {
- var a = left.criteria, b = right.criteria;
- return a < b ? -1 : a > b ? 1 : 0;
- };
- callback(null, _map(results.sort(fn), function (x) {
+ callback(null, _map(results.sort(comparator), function (x) {
return x.value;
}));
}
+
});
+
+ function comparator(left, right) {
+ var a = left.criteria, b = right.criteria;
+ return a < b ? -1 : a > b ? 1 : 0;
+ }
};
- async.auto = function (tasks, callback) {
- callback = callback || function () {};
+ async.auto = function (tasks, concurrency, callback) {
+ if (typeof arguments[1] === 'function') {
+ // concurrency is optional, shift the args.
+ callback = concurrency;
+ concurrency = null;
+ }
+ callback = _once(callback || noop);
var keys = _keys(tasks);
- var remainingTasks = keys.length
+ var remainingTasks = keys.length;
if (!remainingTasks) {
- return callback();
+ return callback(null);
+ }
+ if (!concurrency) {
+ concurrency = remainingTasks;
}
var results = {};
+ var runningTasks = 0;
+
+ var hasError = false;
var listeners = [];
- var addListener = function (fn) {
+ function addListener(fn) {
listeners.unshift(fn);
- };
- var removeListener = function (fn) {
- for (var i = 0; i < listeners.length; i += 1) {
- if (listeners[i] === fn) {
- listeners.splice(i, 1);
- return;
- }
- }
- };
- var taskComplete = function () {
- remainingTasks--
- _each(listeners.slice(0), function (fn) {
+ }
+ function removeListener(fn) {
+ var idx = _indexOf(listeners, fn);
+ if (idx >= 0) listeners.splice(idx, 1);
+ }
+ function taskComplete() {
+ remainingTasks--;
+ _arrayEach(listeners.slice(0), function (fn) {
fn();
});
- };
+ }
addListener(function () {
if (!remainingTasks) {
- var theCallback = callback;
- // prevent final callback from calling itself if it errors
- callback = function () {};
-
- theCallback(null, results);
+ callback(null, results);
}
});
- _each(keys, function (k) {
+ _arrayEach(keys, function (k) {
+ if (hasError) return;
var task = _isArray(tasks[k]) ? tasks[k]: [tasks[k]];
- var taskCallback = function (err) {
- var args = Array.prototype.slice.call(arguments, 1);
+ var taskCallback = _restParam(function(err, args) {
+ runningTasks--;
if (args.length <= 1) {
args = args[0];
}
if (err) {
var safeResults = {};
- _each(_keys(results), function(rkey) {
- safeResults[rkey] = results[rkey];
+ _forEachOf(results, function(val, rkey) {
+ safeResults[rkey] = val;
});
safeResults[k] = args;
+ hasError = true;
+
callback(err, safeResults);
- // stop subsequent errors hitting callback multiple times
- callback = function () {};
}
else {
results[k] = args;
async.setImmediate(taskComplete);
}
- };
- var requires = task.slice(0, Math.abs(task.length - 1)) || [];
- var ready = function () {
- return _reduce(requires, function (a, x) {
+ });
+ var requires = task.slice(0, 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 in ' + requires.join(', '));
+ }
+ if (_isArray(dep) && _indexOf(dep, k) >= 0) {
+ throw new Error('Has cyclic dependencies');
+ }
+ }
+ function ready() {
+ return runningTasks < concurrency && _reduce(requires, function (a, x) {
return (a && results.hasOwnProperty(x));
}, true) && !results.hasOwnProperty(k);
- };
+ }
if (ready()) {
+ runningTasks++;
task[task.length - 1](taskCallback, results);
}
else {
- var listener = function () {
- if (ready()) {
- removeListener(listener);
- task[task.length - 1](taskCallback, results);
- }
- };
addListener(listener);
}
+ function listener() {
+ if (ready()) {
+ runningTasks++;
+ removeListener(listener);
+ task[task.length - 1](taskCallback, results);
+ }
+ }
});
};
- var _argsRegEx = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
- var _diArgs = function(func) {
- var result = func.toString().match(_argsRegEx)[1].split(',');
- for (var i = 0, e = result.length; i != e; ++i)
- result[i] = result[i].trim();
- return result;
- };
-
- async.autoInject = function(tasks, callback) {
- var injTasks = {};
- for (var i in tasks)
- injTasks[i] = (function(val) {
- if (!(val instanceof Function))
- return val;
-
- var args = _diArgs(val);
- if (args.length < 2)
- return val;
-
- args.shift();
-
- return args.concat(function(cb, results) {
- val.apply(null, [cb].concat(_map(args, function(a) { return results[a]; })));
- });
- })(tasks[i]);
-
- return this.auto(
- injTasks,
- callback && function(err, results) {
- callback.apply(null, [err].concat(_map(_diArgs(callback).slice(1), function(a) { return results[a]; })));
- }
- );
- };
+ var _argsRegEx = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
+ var _diArgs = function(func) {
+ var result = func.toString().match(_argsRegEx)[1].split(',');
+ for (var i = 0, e = result.length; i != e; ++i)
+ result[i] = result[i].trim();
+ return result;
+ };
+
+ async.autoInject = function(tasks, callback) {
+ var injTasks = {};
+ for (var i in tasks)
+ injTasks[i] = (function(val) {
+ if (!(val instanceof Function))
+ return val;
+
+ var args = _diArgs(val);
+ if (args.length < 2)
+ return val;
+
+ args.shift();
+
+ return args.concat(function(cb, results) {
+ val.apply(null, [cb].concat(_map(args, function(a) { return results[a]; })));
+ });
+ })(tasks[i]);
+
+ return this.auto(
+ injTasks,
+ callback && function(err, results) {
+ callback.apply(null, [err].concat(_map(_diArgs(callback).slice(1), function(a) { return results[a]; })));
+ }
+ );
+ };
async.retry = function(times, task, callback) {
var DEFAULT_TIMES = 5;
+ var DEFAULT_INTERVAL = 0;
+
var attempts = [];
- // Use defaults if times not passed
- if (typeof times === 'function') {
+
+ var opts = {
+ times: DEFAULT_TIMES,
+ interval: DEFAULT_INTERVAL
+ };
+
+ function parseTimes(acc, t){
+ if(typeof t === 'number'){
+ acc.times = parseInt(t, 10) || DEFAULT_TIMES;
+ } else if(typeof t === 'object'){
+ acc.times = parseInt(t.times, 10) || DEFAULT_TIMES;
+ acc.interval = parseInt(t.interval, 10) || DEFAULT_INTERVAL;
+ } else {
+ throw new Error('Unsupported argument type for \'times\': ' + typeof t);
+ }
+ }
+
+ var length = arguments.length;
+ if (length < 1 || length > 3) {
+ throw new Error('Invalid arguments - must be either (task), (task, callback), (times, task) or (times, task, callback)');
+ } else if (length <= 2 && typeof times === 'function') {
callback = task;
task = times;
- times = DEFAULT_TIMES;
}
- // Make sure times is a number
- times = parseInt(times, 10) || DEFAULT_TIMES;
- var wrappedTask = function(wrappedCallback, wrappedResults) {
- var retryAttempt = function(task, finalAttempt) {
+ if (typeof times !== 'function') {
+ parseTimes(opts, times);
+ }
+ opts.callback = callback;
+ opts.task = task;
+
+ function wrappedTask(wrappedCallback, wrappedResults) {
+ function retryAttempt(task, finalAttempt) {
return function(seriesCallback) {
task(function(err, result){
seriesCallback(!err || finalAttempt, {err: err, result: result});
}, wrappedResults);
};
- };
- while (times) {
- attempts.push(retryAttempt(task, !(times-=1)));
}
+
+ function retryInterval(interval){
+ return function(seriesCallback){
+ setTimeout(function(){
+ seriesCallback(null);
+ }, interval);
+ };
+ }
+
+ while (opts.times) {
+
+ var finalAttempt = !(opts.times-=1);
+ attempts.push(retryAttempt(opts.task, finalAttempt));
+ if(!finalAttempt && opts.interval > 0){
+ attempts.push(retryInterval(opts.interval));
+ }
+ }
+
async.series(attempts, function(done, data){
data = data[data.length - 1];
- (wrappedCallback || callback)(data.err, data.result);
+ (wrappedCallback || opts.callback)(data.err, data.result);
});
}
+
// If a callback is passed, run this as a controll flow
- return callback ? wrappedTask() : wrappedTask
+ return opts.callback ? wrappedTask() : wrappedTask;
};
async.waterfall = function (tasks, callback) {
- callback = callback || function () {};
+ callback = _once(callback || noop);
if (!_isArray(tasks)) {
- var err = new Error('First argument to waterfall must be an array of functions');
- return callback(err);
+ var err = new Error('First argument to waterfall must be an array of functions');
+ return callback(err);
}
if (!tasks.length) {
return callback();
}
- var wrapIterator = function (iterator) {
- return function (err) {
+ function wrapIterator(iterator) {
+ return _restParam(function (err, args) {
if (err) {
- callback.apply(null, arguments);
- callback = function () {};
+ callback.apply(null, [err].concat(args));
}
else {
- var args = Array.prototype.slice.call(arguments, 1);
var next = iterator.next();
if (next) {
args.push(wrapIterator(next));
@@ -587,260 +735,254 @@
else {
args.push(callback);
}
- async.setImmediate(function () {
- iterator.apply(null, args);
- });
+ ensureAsync(iterator).apply(null, args);
}
- };
- };
+ });
+ }
wrapIterator(async.iterator(tasks))();
};
- var _parallel = function(eachfn, tasks, callback) {
- callback = callback || function () {};
- if (_isArray(tasks)) {
- eachfn.map(tasks, function (fn, callback) {
- if (fn) {
- fn(function (err) {
- var args = Array.prototype.slice.call(arguments, 1);
- if (args.length <= 1) {
- args = args[0];
- }
- callback.call(null, err, args);
- });
+ function _parallel(eachfn, tasks, callback) {
+ callback = callback || noop;
+ var results = _isArrayLike(tasks) ? [] : {};
+
+ eachfn(tasks, function (task, key, callback) {
+ task(_restParam(function (err, args) {
+ if (args.length <= 1) {
+ args = args[0];
}
- }, callback);
- }
- else {
- var results = {};
- eachfn.each(_keys(tasks), function (k, callback) {
- tasks[k](function (err) {
- var args = Array.prototype.slice.call(arguments, 1);
- if (args.length <= 1) {
- args = args[0];
- }
- results[k] = args;
- callback(err);
- });
- }, function (err) {
- callback(err, results);
- });
- }
- };
+ results[key] = args;
+ callback(err);
+ }));
+ }, function (err) {
+ callback(err, results);
+ });
+ }
async.parallel = function (tasks, callback) {
- _parallel({ map: async.map, each: async.each }, tasks, callback);
+ _parallel(async.eachOf, tasks, callback);
};
async.parallelLimit = function(tasks, limit, callback) {
- _parallel({ map: _mapLimit(limit), each: _eachLimit(limit) }, tasks, callback);
+ _parallel(_eachOfLimit(limit), tasks, callback);
};
- async.series = function (tasks, callback) {
- callback = callback || function () {};
- if (_isArray(tasks)) {
- async.mapSeries(tasks, function (fn, callback) {
- if (fn) {
- fn(function (err) {
- var args = Array.prototype.slice.call(arguments, 1);
- if (args.length <= 1) {
- args = args[0];
- }
- callback.call(null, err, args);
- });
- }
- }, callback);
- }
- else {
- var results = {};
- async.eachSeries(_keys(tasks), function (k, callback) {
- tasks[k](function (err) {
- var args = Array.prototype.slice.call(arguments, 1);
- if (args.length <= 1) {
- args = args[0];
- }
- results[k] = args;
- callback(err);
- });
- }, function (err) {
- callback(err, results);
- });
- }
+ async.series = function(tasks, callback) {
+ _parallel(async.eachOfSeries, tasks, callback);
};
async.iterator = function (tasks) {
- var makeCallback = function (index) {
- var fn = function () {
+ function makeCallback(index) {
+ function fn() {
if (tasks.length) {
tasks[index].apply(null, arguments);
}
return fn.next();
- };
+ }
fn.next = function () {
return (index < tasks.length - 1) ? makeCallback(index + 1): null;
};
return fn;
- };
+ }
return makeCallback(0);
};
- async.apply = function (fn) {
- var args = Array.prototype.slice.call(arguments, 1);
- return function () {
+ async.apply = _restParam(function (fn, args) {
+ return _restParam(function (callArgs) {
return fn.apply(
- null, args.concat(Array.prototype.slice.call(arguments))
+ null, args.concat(callArgs)
);
- };
- };
+ });
+ });
- var _concat = function (eachfn, arr, fn, callback) {
- var r = [];
- eachfn(arr, function (x, cb) {
+ function _concat(eachfn, arr, fn, callback) {
+ var result = [];
+ eachfn(arr, function (x, index, cb) {
fn(x, function (err, y) {
- r = r.concat(y || []);
+ result = result.concat(y || []);
cb(err);
});
}, function (err) {
- callback(err, r);
+ callback(err, result);
});
- };
+ }
async.concat = doParallel(_concat);
async.concatSeries = doSeries(_concat);
async.whilst = function (test, iterator, callback) {
+ callback = callback || noop;
if (test()) {
- iterator(function (err) {
+ var next = _restParam(function(err, args) {
if (err) {
- return callback(err);
+ callback(err);
+ } else if (test.apply(this, args)) {
+ iterator(next);
+ } else {
+ callback.apply(null, [null].concat(args));
}
- async.whilst(test, iterator, callback);
});
- }
- else {
- callback();
+ iterator(next);
+ } else {
+ callback(null);
}
};
async.doWhilst = function (iterator, test, callback) {
- iterator(function (err) {
- if (err) {
- return callback(err);
- }
- var args = Array.prototype.slice.call(arguments, 1);
- if (test.apply(null, args)) {
- async.doWhilst(iterator, test, callback);
- }
- else {
- callback();
- }
- });
+ var calls = 0;
+ return async.whilst(function() {
+ return ++calls <= 1 || test.apply(this, arguments);
+ }, iterator, callback);
};
async.until = function (test, iterator, callback) {
- if (!test()) {
- iterator(function (err) {
- if (err) {
- return callback(err);
- }
- async.until(test, iterator, callback);
- });
- }
- else {
- callback();
- }
+ return async.whilst(function() {
+ return !test.apply(this, arguments);
+ }, iterator, callback);
};
async.doUntil = function (iterator, test, callback) {
- iterator(function (err) {
+ return async.doWhilst(iterator, function() {
+ return !test.apply(this, arguments);
+ }, callback);
+ };
+
+ async.during = function (test, iterator, callback) {
+ callback = callback || noop;
+
+ var next = _restParam(function(err, args) {
if (err) {
- return callback(err);
+ callback(err);
+ } else {
+ args.push(check);
+ test.apply(this, args);
}
- var args = Array.prototype.slice.call(arguments, 1);
- if (!test.apply(null, args)) {
- async.doUntil(iterator, test, callback);
+ });
+
+ var check = function(err, truth) {
+ if (err) {
+ callback(err);
+ } else if (truth) {
+ iterator(next);
+ } else {
+ callback(null);
}
- else {
- callback();
+ };
+
+ test(check);
+ };
+
+ async.doDuring = function (iterator, test, callback) {
+ var calls = 0;
+ async.during(function(next) {
+ if (calls++ < 1) {
+ next(null, true);
+ } else {
+ test.apply(this, arguments);
}
- });
+ }, iterator, callback);
};
- async.queue = function (worker, concurrency) {
- if (concurrency === undefined) {
+ function _queue(worker, concurrency, payload) {
+ if (concurrency == null) {
concurrency = 1;
}
+ else if(concurrency === 0) {
+ throw new Error('Concurrency must not be zero');
+ }
function _insert(q, data, pos, callback) {
- if (!q.started){
+ if (callback != null && typeof callback !== "function") {
+ throw new Error("task callback must be a function");
+ }
q.started = true;
- }
- if (!_isArray(data)) {
- data = [data];
- }
- if(data.length == 0) {
- // call drain immediately if there are no tasks
- return async.setImmediate(function() {
- if (q.drain) {
- q.drain();
- }
- });
- }
- _each(data, function(task) {
- var item = {
- data: task,
- callback: typeof callback === 'function' ? callback : null
- };
-
- if (pos) {
- q.tasks.unshift(item);
- } else {
- q.tasks.push(item);
- }
-
- if (q.saturated && q.tasks.length === q.concurrency) {
- q.saturated();
- }
- async.setImmediate(q.process);
- });
+ if (!_isArray(data)) {
+ data = [data];
+ }
+ if(data.length === 0 && q.idle()) {
+ // call drain immediately if there are no tasks
+ return async.setImmediate(function() {
+ q.drain();
+ });
+ }
+ _arrayEach(data, function(task) {
+ var item = {
+ data: task,
+ callback: callback || noop
+ };
+
+ if (pos) {
+ q.tasks.unshift(item);
+ } else {
+ q.tasks.push(item);
+ }
+
+ if (q.tasks.length === q.concurrency) {
+ q.saturated();
+ }
+ });
+ async.setImmediate(q.process);
+ }
+ function _next(q, tasks) {
+ return function(){
+ workers -= 1;
+
+ var removed = false;
+ var args = arguments;
+ _arrayEach(tasks, function (task) {
+ _arrayEach(workersList, function (worker, index) {
+ if (worker === task && !removed) {
+ workersList.splice(index, 1);
+ removed = true;
+ }
+ });
+
+ task.callback.apply(task, args);
+ });
+ if (q.tasks.length + workers === 0) {
+ q.drain();
+ }
+ q.process();
+ };
}
var workers = 0;
+ var workersList = [];
var q = {
tasks: [],
concurrency: concurrency,
- saturated: null,
- empty: null,
- drain: null,
+ payload: payload,
+ saturated: noop,
+ empty: noop,
+ drain: noop,
started: false,
paused: false,
push: function (data, callback) {
- _insert(q, data, false, callback);
+ _insert(q, data, false, callback);
},
kill: function () {
- q.drain = null;
- q.tasks = [];
+ q.drain = noop;
+ q.tasks = [];
},
unshift: function (data, callback) {
- _insert(q, data, true, callback);
+ _insert(q, data, true, callback);
},
process: function () {
- if (!q.paused && workers < q.concurrency && q.tasks.length) {
- var task = q.tasks.shift();
- if (q.empty && q.tasks.length === 0) {
+ while(!q.paused && workers < q.concurrency && q.tasks.length){
+
+ var tasks = q.payload ?
+ q.tasks.splice(0, q.payload) :
+ q.tasks.splice(0, q.tasks.length);
+
+ var data = _map(tasks, function (task) {
+ return task.data;
+ });
+
+ if (q.tasks.length === 0) {
q.empty();
}
workers += 1;
- var next = function () {
- workers -= 1;
- if (task.callback) {
- task.callback.apply(task, arguments);
- }
- if (q.drain && q.tasks.length + workers === 0) {
- q.drain();
- }
- q.process();
- };
- var cb = only_once(next);
- worker(task.data, cb);
+ workersList.push(tasks[0]);
+ var cb = only_once(_next(q, tasks));
+ worker(data, cb);
}
},
length: function () {
@@ -849,75 +991,85 @@
running: function () {
return workers;
},
+ workersList: function () {
+ return workersList;
+ },
idle: function() {
return q.tasks.length + workers === 0;
},
pause: function () {
- if (q.paused === true) { return; }
q.paused = true;
},
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);
}
}
};
return q;
+ }
+
+ async.queue = function (worker, concurrency) {
+ var q = _queue(function (items, cb) {
+ worker(items[0], cb);
+ }, concurrency, 1);
+
+ return q;
};
async.priorityQueue = function (worker, concurrency) {
function _compareTasks(a, b){
- return a.priority - b.priority;
- };
+ return a.priority - b.priority;
+ }
function _binarySearch(sequence, item, compare) {
- var beg = -1,
- end = sequence.length - 1;
- while (beg < end) {
- var mid = beg + ((end - beg + 1) >>> 1);
- if (compare(item, sequence[mid]) >= 0) {
- beg = mid;
- } else {
- end = mid - 1;
+ var beg = -1,
+ end = sequence.length - 1;
+ while (beg < end) {
+ var mid = beg + ((end - beg + 1) >>> 1);
+ if (compare(item, sequence[mid]) >= 0) {
+ beg = mid;
+ } else {
+ end = mid - 1;
+ }
}
- }
- return beg;
+ return beg;
}
function _insert(q, data, priority, callback) {
- if (!q.started){
+ if (callback != null && typeof callback !== "function") {
+ throw new Error("task callback must be a function");
+ }
q.started = true;
- }
- if (!_isArray(data)) {
- data = [data];
- }
- if(data.length == 0) {
- // call drain immediately if there are no tasks
- return async.setImmediate(function() {
- if (q.drain) {
- q.drain();
- }
- });
- }
- _each(data, function(task) {
- var item = {
- data: task,
- priority: priority,
- callback: typeof callback === 'function' ? callback : null
- };
-
- q.tasks.splice(_binarySearch(q.tasks, item, _compareTasks) + 1, 0, item);
-
- if (q.saturated && q.tasks.length === q.concurrency) {
- q.saturated();
- }
- async.setImmediate(q.process);
- });
+ if (!_isArray(data)) {
+ data = [data];
+ }
+ if(data.length === 0) {
+ // call drain immediately if there are no tasks
+ return async.setImmediate(function() {
+ q.drain();
+ });
+ }
+ _arrayEach(data, function(task) {
+ var item = {
+ data: task,
+ priority: priority,
+ callback: typeof callback === 'function' ? callback : noop
+ };
+
+ q.tasks.splice(_binarySearch(q.tasks, item, _compareTasks) + 1, 0, item);
+
+ if (q.tasks.length === q.concurrency) {
+ q.saturated();
+ }
+ async.setImmediate(q.process);
+ });
}
// Start with a normal queue
@@ -925,7 +1077,7 @@
// Override push to accept second parameter representing priority
q.push = function (data, priority, callback) {
- _insert(q, data, priority, callback);
+ _insert(q, data, priority, callback);
};
// Remove unshift function
@@ -935,93 +1087,27 @@
};
async.cargo = function (worker, payload) {
- var working = false,
- tasks = [];
-
- var cargo = {
- tasks: tasks,
- payload: payload,
- saturated: null,
- empty: null,
- drain: null,
- drained: true,
- push: function (data, callback) {
- if (!_isArray(data)) {
- data = [data];
- }
- _each(data, function(task) {
- tasks.push({
- data: task,
- callback: typeof callback === 'function' ? callback : null
- });
- cargo.drained = false;
- if (cargo.saturated && tasks.length === payload) {
- cargo.saturated();
- }
- });
- async.setImmediate(cargo.process);
- },
- process: function process() {
- if (working) return;
- if (tasks.length === 0) {
- if(cargo.drain && !cargo.drained) cargo.drain();
- cargo.drained = true;
- return;
- }
-
- var ts = typeof payload === 'number'
- ? tasks.splice(0, payload)
- : tasks.splice(0, tasks.length);
-
- var ds = _map(ts, function (task) {
- return task.data;
- });
-
- if(cargo.empty) cargo.empty();
- working = true;
- worker(ds, function () {
- working = false;
-
- var args = arguments;
- _each(ts, function (data) {
- if (data.callback) {
- data.callback.apply(null, args);
- }
- });
-
- process();
- });
- },
- length: function () {
- return tasks.length;
- },
- running: function () {
- return working;
- }
- };
- return cargo;
+ return _queue(worker, 1, payload);
};
- var _console_fn = function (name) {
- return function (fn) {
- var args = Array.prototype.slice.call(arguments, 1);
- fn.apply(null, args.concat([function (err) {
- var args = Array.prototype.slice.call(arguments, 1);
- if (typeof console !== 'undefined') {
+ function _console_fn(name) {
+ return _restParam(function (fn, args) {
+ fn.apply(null, args.concat([_restParam(function (err, args) {
+ if (typeof console === 'object') {
if (err) {
if (console.error) {
console.error(err);
}
}
else if (console[name]) {
- _each(args, function (x) {
+ _arrayEach(args, function (x) {
console[name](x);
});
}
}
- }]));
- };
- };
+ })]));
+ });
+ }
async.log = _console_fn('log');
async.dir = _console_fn('dir');
/*async.info = _console_fn('info');
@@ -1031,15 +1117,12 @@
async.memoize = function (fn, hasher) {
var memo = {};
var queues = {};
- hasher = hasher || function (x) {
- return x;
- };
- var memoized = function () {
- var args = Array.prototype.slice.call(arguments);
+ hasher = hasher || identity;
+ var memoized = _restParam(function memoized(args) {
var callback = args.pop();
var key = hasher.apply(null, args);
if (key in memo) {
- async.nextTick(function () {
+ async.setImmediate(function () {
callback.apply(null, memo[key]);
});
}
@@ -1048,106 +1131,159 @@
}
else {
queues[key] = [callback];
- fn.apply(null, args.concat([function () {
- memo[key] = arguments;
+ fn.apply(null, args.concat([_restParam(function (args) {
+ memo[key] = args;
var q = queues[key];
delete queues[key];
for (var i = 0, l = q.length; i < l; i++) {
- q[i].apply(null, arguments);
+ q[i].apply(null, args);
}
- }]));
+ })]));
}
- };
+ });
memoized.memo = memo;
memoized.unmemoized = fn;
return memoized;
};
async.unmemoize = function (fn) {
- return function () {
- return (fn.unmemoized || fn).apply(null, arguments);
- };
+ return function () {
+ return (fn.unmemoized || fn).apply(null, arguments);
+ };
};
- async.times = function (count, iterator, callback) {
- var counter = [];
- for (var i = 0; i < count; i++) {
- counter.push(i);
- }
- return async.map(counter, iterator, callback);
- };
+ function _times(mapper) {
+ return function (count, iterator, callback) {
+ mapper(_range(count), iterator, callback);
+ };
+ }
- async.timesSeries = function (count, iterator, callback) {
- var counter = [];
- for (var i = 0; i < count; i++) {
- counter.push(i);
- }
- return async.mapSeries(counter, iterator, callback);
+ async.times = _times(async.map);
+ async.timesSeries = _times(async.mapSeries);
+ async.timesLimit = function (count, limit, iterator, callback) {
+ return async.mapLimit(_range(count), limit, iterator, callback);
};
async.seq = function (/* functions... */) {
var fns = arguments;
- return function () {
+ return _restParam(function (args) {
var that = this;
- var args = Array.prototype.slice.call(arguments);
- var callback = args.pop();
+
+ var callback = args[args.length - 1];
+ if (typeof callback == 'function') {
+ args.pop();
+ } else {
+ callback = noop;
+ }
+
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);
+ fn.apply(that, newargs.concat([_restParam(function (err, nextargs) {
cb(err, nextargs);
- }]))
+ })]));
},
function (err, results) {
callback.apply(that, [err].concat(results));
});
- };
+ });
};
async.compose = function (/* functions... */) {
- return async.seq.apply(null, Array.prototype.reverse.call(arguments));
+ return async.seq.apply(null, Array.prototype.reverse.call(arguments));
};
- var _applyEach = function (eachfn, fns /*args...*/) {
- var go = function () {
- var that = this;
- var args = Array.prototype.slice.call(arguments);
- var callback = args.pop();
- return eachfn(fns, function (fn, cb) {
- fn.apply(that, args.concat([cb]));
- },
- callback);
- };
- if (arguments.length > 2) {
- var args = Array.prototype.slice.call(arguments, 2);
- return go.apply(this, args);
- }
- else {
- return go;
- }
- };
- async.applyEach = doParallel(_applyEach);
- async.applyEachSeries = doSeries(_applyEach);
+
+ function _applyEach(eachfn) {
+ return _restParam(function(fns, args) {
+ var go = _restParam(function(args) {
+ var that = this;
+ var callback = args.pop();
+ return eachfn(fns, function (fn, _, cb) {
+ fn.apply(that, args.concat([cb]));
+ },
+ callback);
+ });
+ if (args.length) {
+ return go.apply(this, args);
+ }
+ else {
+ return go;
+ }
+ });
+ }
+
+ async.applyEach = _applyEach(async.eachOf);
+ async.applyEachSeries = _applyEach(async.eachOfSeries);
+
async.forever = function (fn, callback) {
+ var done = only_once(callback || noop);
+ var task = ensureAsync(fn);
function next(err) {
if (err) {
- if (callback) {
- return callback(err);
- }
- throw err;
+ return done(err);
}
- fn(next);
+ task(next);
}
next();
};
+ function ensureAsync(fn) {
+ return _restParam(function (args) {
+ var callback = args.pop();
+ args.push(function () {
+ var innerArgs = arguments;
+ if (sync) {
+ async.setImmediate(function () {
+ callback.apply(null, innerArgs);
+ });
+ } else {
+ callback.apply(null, innerArgs);
+ }
+ });
+ var sync = true;
+ fn.apply(this, args);
+ sync = false;
+ });
+ }
+
+ async.ensureAsync = ensureAsync;
+
+ async.constant = _restParam(function(values) {
+ var args = [null].concat(values);
+ return function (callback) {
+ return callback.apply(this, args);
+ };
+ });
+
+ async.wrapSync =
+ async.asyncify = function asyncify(func) {
+ return _restParam(function (args) {
+ var callback = args.pop();
+ var result;
+ try {
+ result = func.apply(this, args);
+ } catch (e) {
+ return callback(e);
+ }
+ // if result is Promise object
+ if (_isObject(result) && typeof result.then === "function") {
+ result.then(function(value) {
+ callback(null, value);
+ })["catch"](function(err) {
+ callback(err.message ? err : new Error(err));
+ });
+ } else {
+ callback(null, result);
+ }
+ });
+ };
+
// Node.js
- if (typeof module !== 'undefined' && module.exports) {
+ if (typeof module === 'object' && module.exports) {
module.exports = async;
}
// AMD / RequireJS
- else if (typeof define !== 'undefined' && define.amd) {
+ else if (typeof define === 'function' && define.amd) {
define([], function () {
return async;
});
diff --git a/mocha_test/.jshintrc b/mocha_test/.jshintrc
new file mode 100644
index 0000000..ea272bc
--- /dev/null
+++ b/mocha_test/.jshintrc
@@ -0,0 +1,4 @@
+{
+ "mocha": true,
+ "expr": true
+} \ No newline at end of file
diff --git a/mocha_test/compose.js b/mocha_test/compose.js
new file mode 100644
index 0000000..85306a5
--- /dev/null
+++ b/mocha_test/compose.js
@@ -0,0 +1,86 @@
+var async = require('../lib/async');
+var expect = require('chai').expect;
+
+describe('compose', function(){
+ context('all functions succeed', function(){
+ it('yields the result of the composition of the functions', function(done){
+ var add2 = function (n, cb) {
+ setTimeout(function () {
+ cb(null, n + 2);
+ });
+ };
+ var mul3 = function (n, cb) {
+ setTimeout(function () {
+ cb(null, n * 3);
+ });
+ };
+ var add1 = function (n, cb) {
+ setTimeout(function () {
+ cb(null, n + 1);
+ });
+ };
+ var add2mul3add1 = async.compose(add1, mul3, add2);
+ add2mul3add1(3, function (err, result) {
+ expect(err).to.not.exist;
+ expect(result).to.eql(16);
+ done();
+ });
+ });
+ });
+
+ context('a function errors', function(){
+ it('yields the error and does not call later functions', function(done){
+ var add1called = false;
+ var mul3error = new Error('mul3 error');
+ var add2 = function (n, cb) {
+ setTimeout(function () {
+ cb(null, n + 2);
+ });
+ };
+ var mul3 = function (n, cb) {
+ setTimeout(function () {
+ cb(mul3error);
+ });
+ };
+ var add1 = function (n, cb) {
+ add1called = true;
+ setTimeout(function () {
+ cb(null, n + 1);
+ });
+ };
+ var add2mul3add1 = async.compose(add1, mul3, add2);
+ add2mul3add1(3, function (err, result) {
+ expect(err).to.eql(mul3error);
+ expect(result).to.not.exist;
+ expect(add1called).to.be.false;
+ done();
+ });
+ });
+ });
+
+ it('calls each function with the binding of the composed function', function(done){
+ var context = {};
+ var add2Context = null;
+ var mul3Context = null;
+ var add2 = function (n, cb) {
+ add2Context = this;
+ setTimeout(function () {
+ cb(null, n + 2);
+ });
+ };
+ var mul3 = function (n, cb) {
+ mul3Context = this;
+ setTimeout(function () {
+ cb(null, n * 3);
+ });
+ };
+ var add2mul3 = async.compose(mul3, add2);
+ add2mul3.call(context, 3, function (err, result) {
+ expect(err).to.not.exist;
+ expect(result).to.eql(15);
+ expect(add2Context).to.equal(context);
+ expect(mul3Context).to.equal(context);
+ done();
+ });
+ });
+});
diff --git a/mocha_test/forever.js b/mocha_test/forever.js
new file mode 100644
index 0000000..970c422
--- /dev/null
+++ b/mocha_test/forever.js
@@ -0,0 +1,44 @@
+var async = require('../lib/async');
+var expect = require('chai').expect;
+var isBrowser = require('./support/is_browser');
+
+describe('forever', function(){
+ context('function is asynchronous', function(){
+ it('executes the function over and over until it yields an error', function(done){
+ var counter = 0;
+ function addOne(callback) {
+ counter++;
+ if (counter === 50) {
+ return callback('too big!');
+ }
+ async.setImmediate(function () {
+ callback();
+ });
+ }
+ async.forever(addOne, function (err) {
+ expect(err).to.eql('too big!');
+ expect(counter).to.eql(50);
+ done();
+ });
+ });
+ });
+
+ context('function is synchronous', function(){
+ it('does not cause a stack overflow', function(done){
+ if (isBrowser()) return done(); // this will take forever in a browser
+ var counter = 0;
+ function addOne(callback) {
+ counter++;
+ if (counter === 50000) { // needs to be huge to potentially overflow stack in node
+ return callback('too big!');
+ }
+ callback();
+ }
+ async.forever(addOne, function (err) {
+ expect(err).to.eql('too big!');
+ expect(counter).to.eql(50000);
+ done();
+ });
+ });
+ });
+});
diff --git a/mocha_test/support/is_browser.js b/mocha_test/support/is_browser.js
new file mode 100644
index 0000000..85e1522
--- /dev/null
+++ b/mocha_test/support/is_browser.js
@@ -0,0 +1,4 @@
+module.exports = function() {
+ return (typeof process === "undefined") ||
+ (process + "" !== "[object process]"); // browserify
+};
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 09f76aa..2d366b8 100644
--- a/package.json
+++ b/package.json
@@ -1,36 +1,85 @@
{
- "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",
+ "files": [
+ "lib",
+ "dist/async.js",
+ "dist/async.min.js"
+ ],
+ "author": "Caolan McMahon",
+ "version": "1.5.1",
+ "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": {
+ "benchmark": "bestiejs/benchmark.js",
+ "bluebird": "^2.9.32",
+ "chai": "^3.1.0",
+ "coveralls": "^2.11.2",
+ "es6-promise": "^2.3.0",
+ "jscs": "^1.13.1",
+ "jshint": "~2.8.0",
+ "karma": "^0.13.2",
+ "karma-browserify": "^4.2.1",
+ "karma-firefox-launcher": "^0.1.6",
+ "karma-mocha": "^0.2.0",
+ "karma-mocha-reporter": "^1.0.2",
+ "lodash": "^3.9.0",
+ "mkdirp": "~0.5.1",
+ "mocha": "^2.2.5",
+ "native-promise-only": "^0.8.0-a",
+ "nodeunit": ">0.0.0",
+ "nyc": "^2.1.0",
+ "rsvp": "^3.0.18",
+ "semver": "^4.3.6",
+ "uglify-js": "~2.4.0",
+ "xyz": "^0.5.0",
+ "yargs": "~3.9.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": {
+ "mocha-node-test": "mocha mocha_test/",
+ "mocha-browser-test": "karma start",
+ "mocha-test": "npm run mocha-node-test && npm run mocha-browser-test",
+ "nodeunit-test": "nodeunit test/test-async.js",
+ "test": "npm run-script lint && npm run nodeunit-test && npm run mocha-test",
+ "lint": "jshint lib/*.js test/*.js perf/*.js && jscs lib/*.js test/*.js perf/*.js",
+ "coverage": "nyc npm test && nyc report",
+ "coveralls": "nyc npm test && nyc report --reporter=text-lcov | coveralls"
+ },
+ "spm": {
+ "main": "lib/async.js"
+ },
+ "volo": {
+ "main": "lib/async.js",
+ "ignore": [
+ "**/.*",
+ "node_modules",
+ "bower_components",
+ "test",
+ "tests"
+ ]
+ }
}
diff --git a/perf/benchmark.js b/perf/benchmark.js
new file mode 100755
index 0000000..f75e05d
--- /dev/null
+++ b/perf/benchmark.js
@@ -0,0 +1,228 @@
+#!/usr/bin/env node
+
+var _ = require("lodash");
+var Benchmark = require("benchmark");
+var exec = require("child_process").exec;
+var execSync = require("child_process").execSync;
+var fs = require("fs");
+var path = require("path");
+var mkdirp = require("mkdirp");
+var async = require("../");
+var suiteConfigs = require("./suites");
+var semver = require("semver");
+
+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("i", "reject")
+ .default("i", "^$")
+ .describe("l", "maximum running time per test (in seconds)")
+ .alias("l", "limit")
+ .default("l", 2)
+ .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");
+
+function getLatestVersion() {
+ var tags = execSync("git tag");
+ var latest = _(tags).split("\n")
+ .compact()
+ .sort(semver.gt)
+ .last();
+ console.log("Latest tag is ", latest);
+ return latest;
+}
+
+var version0 = args._[0] || getLatestVersion();
+var version1 = args._[1] || "current";
+var versionNames = [version0, version1];
+var benchOptions = {defer: true, minSamples: 1, maxTime: +args.l};
+var versions;
+var wins = {};
+var totalTime = {};
+totalTime[version0] = wins[version0] = 0;
+totalTime[version1] = wins[version1] = 0;
+
+console.log("Comparing " + version0 + " with " + version1 +
+ " on Node " + process.version);
+console.log("--------------------------------------");
+
+
+async.eachSeries(versionNames, cloneVersion, function (err) {
+ if (err) { throw 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;
+ var errored = false;
+
+ function addBench(version, versionName) {
+ var name = suiteConfig.name + " " + versionName;
+
+ try {
+ suiteConfig.setup(1);
+ suiteConfig.fn(version, function () {});
+ } catch (e) {
+ console.error(name + " Errored");
+ errored = true;
+ return;
+ }
+
+ suite.add(name, function (deferred) {
+ suiteConfig.fn(version, function () {
+ deferred.resolve();
+ });
+ }, _.extend({
+ versionName: versionName,
+ setup: _.partial.apply(null, [suiteConfig.setup].concat(args)),
+ onError: function (err) {
+ console.log(err.stack);
+ }
+ }, 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(3) + "ms per run");
+ var version = event.target.options.versionName;
+ if (errored) return;
+ totalTime[version] += mean;
+ })
+ .on('error', function (err) { console.error(err); })
+ .on('complete', function() {
+ if (!errored) {
+ 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) {
+ if (err) {
+ throw err;
+ }
+ callback();
+ });
+
+ });
+}
diff --git a/perf/memory.js b/perf/memory.js
new file mode 100644
index 0000000..1a022af
--- /dev/null
+++ b/perf/memory.js
@@ -0,0 +1,46 @@
+if (process.execArgv[0] !== "--expose-gc") {
+ console.error("please run with node --expose-gc");
+ process.exit(1);
+}
+
+var async = require("../");
+global.gc();
+var startMem = process.memoryUsage().heapUsed;
+
+function waterfallTest(cb) {
+ var functions = [];
+
+ for(var i = 0; i < 10000; i++) {
+ functions.push(function leaky(next) {
+ function func1(cb) {return cb(); }
+
+ function func2(callback) {
+ if (true) {
+ callback();
+ //return next(); // Should be callback here.
+ }
+ }
+
+ function func3(cb) {return cb(); }
+
+ async.waterfall([
+ func1,
+ func2,
+ func3
+ ], next);
+ });
+ }
+
+ async.parallel(functions, cb);
+}
+
+function reportMemory() {
+ global.gc();
+ var increase = process.memoryUsage().heapUsed - startMem;
+ console.log("memory increase: " +
+ (+(increase / 1024).toPrecision(3)) + "kB");
+}
+
+waterfallTest(function () {
+ setTimeout(reportMemory, 0);
+});
diff --git a/perf/suites.js b/perf/suites.js
new file mode 100644
index 0000000..dd8392c
--- /dev/null
+++ b/perf/suites.js
@@ -0,0 +1,297 @@
+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) {
+ setImmediate(function () {
+ 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: "some - no short circuit- false",
+ // args lists are passed to the setup function
+ args: [[500]],
+ setup: function(count) {
+ tasks = _.range(count);
+ },
+ fn: function (async, done) {
+ async.some(tasks, function(i, cb) {
+ async.setImmediate(function() {
+ cb(i >= 600);
+ });
+ }, done);
+ }
+ },
+ {
+ name: "some - short circuit - true",
+ // args lists are passed to the setup function
+ args: [[500]],
+ setup: function(count) {
+ tasks = _.range(count);
+ },
+ fn: function (async, done) {
+ async.some(tasks, function(i, cb) {
+ async.setImmediate(function() {
+ cb(i >= 60);
+ });
+ }, done);
+ }
+ },
+ {
+ name: "every - no short circuit- true",
+ // args lists are passed to the setup function
+ args: [[500]],
+ setup: function(count) {
+ tasks = _.range(count);
+ },
+ fn: function (async, done) {
+ async.every(tasks, function(i, cb) {
+ async.setImmediate(function() {
+ cb(i <= 600);
+ });
+ }, done);
+ }
+ },
+ {
+ name: "every - short circuit - false",
+ // args lists are passed to the setup function
+ args: [[500]],
+ setup: function(count) {
+ tasks = _.range(count);
+ },
+ fn: function (async, done) {
+ async.every(tasks, function(i, cb) {
+ async.setImmediate(function() {
+ cb(i <= 60);
+ });
+ }, done);
+ }
+ },
+ {
+ 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);
+ }
+ },
+ {
+ name: "ensureAsync sync",
+ fn: function (async, done) {
+ async.ensureAsync(function (cb) {
+ cb();
+ })(done);
+ }
+ },
+ {
+ name: "ensureAsync async",
+ fn: function (async, done) {
+ async.ensureAsync(function (cb) {
+ setImmediate(cb);
+ })(done);
+ }
+ },
+ {
+ name: "ensureAsync async noWrap",
+ fn: function (async, done) {
+ (function (cb) {
+ setImmediate(cb);
+ }(done));
+ }
+ }
+];
+
diff --git a/support/sync-package-managers.js b/support/sync-package-managers.js
new file mode 100755
index 0000000..28c22e9
--- /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', 'main', 'keywords',
+ 'license', 'homepage', 'repository', 'devDependencies'];
+
+var componentSpecific = {
+ repository: REPOSITORY_NAME,
+ scripts: [packageJson.main]
+};
+
+var componentInclude = ['name', 'description', 'version', 'keywords',
+ 'license', 'main'];
+
+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 8180119..bead45b 100755
--- a/test/test-async.js
+++ b/test/test-async.js
@@ -1,3 +1,8 @@
+/**
+ * NOTE: We are in the process of migrating these tests to Mocha. If you are
+ * adding a new test, consider creating a new spec file in mocha_tests/
+ */
+
var async = require('../lib/async');
if (!Function.prototype.bind) {
@@ -6,7 +11,7 @@ if (!Function.prototype.bind) {
var self = this;
return function () {
self.apply(thisArg, args.concat(Array.prototype.slice.call(arguments)));
- }
+ };
};
}
@@ -17,6 +22,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 +55,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){
@@ -66,26 +85,13 @@ function getFunctionsObject(call_order) {
};
}
-exports['forever'] = function (test) {
- test.expect(1);
- var counter = 0;
- function addOne(callback) {
- counter++;
- if (counter === 50) {
- return callback('too big!');
- }
- async.setImmediate(function () {
- callback();
- });
- }
- async.forever(addOne, function (err) {
- test.equal(err, 'too big!');
- test.done();
- });
-};
+function isBrowser() {
+ return (typeof process === "undefined") ||
+ (process + "" !== "[object process]"); // browserify
+}
exports['applyEach'] = function (test) {
- test.expect(4);
+ test.expect(5);
var call_order = [];
var one = function (val, cb) {
test.equal(val, 5);
@@ -109,13 +115,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 +146,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();
});
@@ -169,13 +177,14 @@ exports['applyEach partial application'] = function (test) {
}, 150);
};
async.applyEach([one, two, three])(5, function (err) {
+ if (err) throw err;
test.same(call_order, ['two', 'one', 'three']);
test.done();
});
};
-exports['compose'] = function (test) {
- test.expect(4);
+exports['seq'] = function (test) {
+ test.expect(5);
var add2 = function (n, cb) {
test.equal(n, 3);
setTimeout(function () {
@@ -194,17 +203,18 @@ exports['compose'] = function (test) {
cb(null, n + 1);
}, 100);
};
- var add2mul3add1 = async.compose(add1, mul3, add2);
+ var add2mul3add1 = async.seq(add2, mul3, add1);
add2mul3add1(3, function (err, result) {
if (err) {
return test.done(err);
}
+ test.ok(err === null, err + " passed instead of 'null'");
test.equal(result, 16);
test.done();
});
};
-exports['compose error'] = function (test) {
+exports['seq error'] = function (test) {
test.expect(3);
var testerr = new Error('test');
@@ -226,16 +236,15 @@ exports['compose error'] = function (test) {
cb(null, n + 1);
}, 100);
};
- var add2mul3add1 = async.compose(add1, mul3, add2);
- add2mul3add1(3, function (err, result) {
+ var add2mul3add1 = async.seq(add2, mul3, add1);
+ add2mul3add1(3, function (err) {
test.equal(err, testerr);
test.done();
});
};
-exports['compose binding'] = function (test) {
+exports['seq binding'] = function (test) {
test.expect(4);
- var testerr = new Error('test');
var testcontext = {name: 'foo'};
var add2 = function (n, cb) {
@@ -250,7 +259,7 @@ exports['compose binding'] = function (test) {
cb(null, n * 3);
}, 15);
};
- var add2mul3 = async.compose(mul3, add2);
+ var add2mul3 = async.seq(add2, mul3);
add2mul3.call(testcontext, 3, function (err, result) {
if (err) {
return test.done(err);
@@ -261,68 +270,8 @@ exports['compose binding'] = function (test) {
});
};
-exports['seq'] = function (test) {
- test.expect(4);
- var add2 = function (n, cb) {
- test.equal(n, 3);
- setTimeout(function () {
- cb(null, n + 2);
- }, 50);
- };
- var mul3 = function (n, cb) {
- test.equal(n, 5);
- setTimeout(function () {
- cb(null, n * 3);
- }, 15);
- };
- var add1 = function (n, cb) {
- test.equal(n, 15);
- setTimeout(function () {
- cb(null, n + 1);
- }, 100);
- };
- var add2mul3add1 = async.seq(add2, mul3, add1);
- add2mul3add1(3, function (err, result) {
- if (err) {
- return test.done(err);
- }
- test.equal(result, 16);
- test.done();
- });
-};
-
-exports['seq error'] = function (test) {
- test.expect(3);
- var testerr = new Error('test');
-
- var add2 = function (n, cb) {
- test.equal(n, 3);
- setTimeout(function () {
- cb(null, n + 2);
- }, 50);
- };
- var mul3 = function (n, cb) {
- test.equal(n, 5);
- setTimeout(function () {
- cb(testerr);
- }, 15);
- };
- var add1 = function (n, cb) {
- test.ok(false, 'add1 should not get called');
- setTimeout(function () {
- cb(null, n + 1);
- }, 100);
- };
- var add2mul3add1 = async.seq(add2, mul3, add1);
- add2mul3add1(3, function (err, result) {
- test.equal(err, testerr);
- test.done();
- });
-};
-
-exports['seq binding'] = function (test) {
- test.expect(4);
- var testerr = new Error('test');
+exports['seq without callback'] = function (test) {
+ test.expect(2);
var testcontext = {name: 'foo'};
var add2 = function (n, cb) {
@@ -331,26 +280,18 @@ exports['seq binding'] = function (test) {
cb(null, n + 2);
}, 50);
};
- var mul3 = function (n, cb) {
+ var mul3 = function () {
test.equal(this, testcontext);
setTimeout(function () {
- cb(null, n * 3);
+ test.done();
}, 15);
};
var add2mul3 = async.seq(add2, mul3);
- add2mul3.call(testcontext, 3, function (err, result) {
- if (err) {
- return test.done(err);
- }
- test.equal(this, testcontext);
- test.equal(result, 15);
- test.done();
- });
+ add2mul3.call(testcontext, 3);
};
exports['auto'] = function(test){
var callOrder = [];
- var testdata = [{test: 'test'}];
async.auto({
task1: ['task2', function(callback){
setTimeout(function(){
@@ -374,8 +315,8 @@ exports['auto'] = function(test){
}],
task5: ['task2', function(callback){
setTimeout(function(){
- callOrder.push('task5');
- callback();
+ callOrder.push('task5');
+ callback();
}, 0);
}],
task6: ['task2', function(callback){
@@ -384,11 +325,41 @@ 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();
});
};
+exports['auto concurrency'] = function (test) {
+ var concurrency = 2;
+ var runningTasks = [];
+ var makeCallback = function(taskName) {
+ return function(callback) {
+ runningTasks.push(taskName);
+ setTimeout(function(){
+ // Each task returns the array of running tasks as results.
+ var result = runningTasks.slice(0);
+ runningTasks.splice(runningTasks.indexOf(taskName), 1);
+ callback(null, result);
+ });
+ };
+ };
+ async.auto({
+ task1: ['task2', makeCallback('task1')],
+ task2: makeCallback('task2'),
+ task3: ['task2', makeCallback('task3')],
+ task4: ['task1', 'task2', makeCallback('task4')],
+ task5: ['task2', makeCallback('task5')],
+ task6: ['task2', makeCallback('task6')]
+ }, concurrency, function(err, results){
+ Object.keys(results).forEach(function(taskName) {
+ test.ok(results[taskName].length <= concurrency);
+ });
+ test.done();
+ });
+};
+
exports['auto petrify'] = function (test) {
var callOrder = [];
async.auto({
@@ -414,6 +385,7 @@ exports['auto petrify'] = function (test) {
}]
},
function (err) {
+ if (err) throw err;
test.same(callOrder, ['task2', 'task3', 'task1', 'task4']);
test.done();
});
@@ -422,25 +394,25 @@ exports['auto petrify'] = function (test) {
exports['auto results'] = function(test){
var callOrder = [];
async.auto({
- task1: ['task2', function(callback, results){
+ task1: ['task2', function(callback, results){
test.same(results.task2, 'task2');
setTimeout(function(){
callOrder.push('task1');
callback(null, 'task1a', 'task1b');
}, 25);
}],
- task2: function(callback){
+ task2: function(callback){
setTimeout(function(){
callOrder.push('task2');
callback(null, 'task2');
}, 50);
},
- task3: ['task2', function(callback, results){
+ task3: ['task2', function(callback, results){
test.same(results.task2, 'task2');
callOrder.push('task3');
callback(null);
}],
- task4: ['task1', 'task2', function(callback, results){
+ task4: ['task1', 'task2', function(callback, results){
test.same(results.task1, ['task1a','task1b']);
test.same(results.task2, 'task2');
callOrder.push('task4');
@@ -456,6 +428,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();
});
};
@@ -487,6 +460,13 @@ exports['auto no callback'] = function(test){
});
};
+exports['auto concurrency no callback'] = function(test){
+ async.auto({
+ task1: function(callback){callback();},
+ task2: ['task1', function(callback){callback(); test.done();}]
+ }, 1);
+};
+
exports['auto error should pass partial results'] = function(test) {
async.auto({
task1: function(callback){
@@ -495,7 +475,7 @@ exports['auto error should pass partial results'] = function(test) {
task2: ['task1', function(callback){
callback('testerror', 'result2');
}],
- task3: ['task2', function(callback){
+ task3: ['task2', function(){
test.ok(false, 'task3 should not be called');
}]
},
@@ -503,7 +483,7 @@ exports['auto error should pass partial results'] = function(test) {
test.equals(err, 'testerror');
test.equals(results.task1, 'result1');
test.equals(results.task2, 'result2');
- test.done();
+ test.done();
});
};
@@ -665,7 +645,7 @@ exports['autoInject error should pass partial results'] = function(test) {
test.equals(err, 'testerror');
test.equals(task1, 'result1');
test.equals(task2, 'result2');
- test.done();
+ test.done();
});
};
@@ -673,15 +653,15 @@ exports['autoInject 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() }],
- task2: ['task3', function(callback) { /* by design: DON'T call callback */ }],
+ task1: ['task3', function(/*callback*/) { test.done(); }],
+ task2: ['task3', function(/*callback*/) { /* by design: DON'T call callback */ }],
task3: function(callback) { callback(); }
});
};
// Issue 410 on github: https://github.com/caolan/async/issues/410
exports['auto calls callback multiple times'] = function(test) {
- if (typeof process === 'undefined') {
+ if (isBrowser()) {
// node only test
test.done();
return;
@@ -701,7 +681,7 @@ exports['auto calls callback multiple times'] = function(test) {
},
// Error throwing final callback. This should only run once
- function(err) {
+ function() {
finalCallCount++;
var e = new Error("An error");
e._test_error = true;
@@ -716,11 +696,26 @@ exports['auto calls callback multiple times'] = function(test) {
}, 10);
};
+
+exports['auto calls callback multiple times with parallel functions'] = function(test) {
+ test.expect(1);
+ async.auto({
+ task1: function(callback) { setTimeout(callback,0,"err"); },
+ task2: function(callback) { setTimeout(callback,0,"err"); }
+ },
+ // Error throwing final callback. This should only run once
+ function(err) {
+ test.equal(err, "err");
+ test.done();
+ });
+};
+
+
// Issue 462 on github: https://github.com/caolan/async/issues/462
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){
@@ -735,26 +730,70 @@ 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){
+ 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){
+ callback(null, 'task1');
+ }],
+ task2: ['task1', function(callback){
+ callback(null, 'task2');
+ }]
+ });
+ }, Error);
+ test.done();
+};
+
+// Issue 988 on github: https://github.com/caolan/async/issues/988
+exports['auto stops running tasks on error'] = function(test) {
+ async.auto({
+ task1: function (callback) {
+ callback('error');
+ },
+ task2: function (callback) {
+ test.ok(false, 'test2 should not be called');
+ callback();
+ }
+ }, 1, function (error) {
+ test.equal(error, 'error', 'finishes with 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'
- function fn(callback, results) {
- callCount++
- failed--
- if (!failed) callback(null, expectedResult)
- else callback(true) // respond with error
+ var failed = 3;
+ var callCount = 0;
+ var expectedResult = 'success';
+ function fn(callback) {
+ 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();
});
};
@@ -764,10 +803,10 @@ exports['retry when all attempts succeeds'] = function(test) {
var callCount = 0;
var error = 'ERROR';
var erroredResult = 'RESULT';
- function fn(callback, results) {
+ function fn(callback) {
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");
@@ -776,11 +815,46 @@ exports['retry when all attempts succeeds'] = function(test) {
});
};
+exports['retry fails with invalid arguments'] = function(test) {
+ test.throws(function() {
+ async.retry("");
+ });
+ test.throws(function() {
+ async.retry();
+ });
+ test.throws(function() {
+ async.retry(function() {}, 2, function() {});
+ });
+ test.done();
+};
+
+exports['retry with interval when all attempts succeeds'] = function(test) {
+ var times = 3;
+ var interval = 500;
+ var callCount = 0;
+ var error = 'ERROR';
+ var erroredResult = 'RESULT';
+ function fn(callback) {
+ callCount++;
+ callback(error + callCount, erroredResult + callCount); // respond with indexed values
+ }
+ var start = new Date().getTime();
+ async.retry({ times: times, interval: interval}, fn, function(err, result){
+ var now = new Date().getTime();
+ var duration = now - start;
+ test.ok(duration > (interval * (times -1)), 'did not include interval');
+ test.equal(callCount, 3, "did not retry the correct number of times");
+ test.equal(err, error + times, "Incorrect error was returned");
+ test.equal(result, erroredResult + times, "Incorrect result was returned");
+ test.done();
+ });
+};
+
exports['retry as an embedded task'] = function(test) {
var retryResult = 'RETRY';
var fooResults;
var retryResults;
-
+
async.auto({
foo: function(callback, results){
fooResults = results;
@@ -797,8 +871,29 @@ exports['retry as an embedded task'] = function(test) {
});
};
-exports['waterfall'] = function(test){
- test.expect(6);
+exports['retry as an embedded task with interval'] = function(test) {
+ var start = new Date().getTime();
+ var opts = {times: 5, interval: 100};
+
+ async.auto({
+ foo: function(callback){
+ callback(null, 'FOO');
+ },
+ retry: async.retry(opts, function(callback) {
+ callback('err');
+ })
+ }, function(){
+ var duration = new Date().getTime() - start;
+ var expectedMinimumDuration = (opts.times -1) * opts.interval;
+ test.ok(duration >= expectedMinimumDuration, "The duration should have been greater than " + expectedMinimumDuration + ", but was " + duration);
+ test.done();
+ });
+};
+
+exports['waterfall'] = {
+
+ 'basic': function(test){
+ test.expect(7);
var call_order = [];
async.waterfall([
function(callback){
@@ -824,31 +919,33 @@ 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){
+ if (err) throw 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){
@@ -865,9 +962,9 @@ exports['waterfall async'] = function(test){
test.done();
}
]);
-};
+},
-exports['waterfall error'] = function(test){
+ 'error': function(test){
test.expect(1);
async.waterfall([
function(callback){
@@ -881,9 +978,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){
@@ -900,7 +997,7 @@ exports['waterfall multiple callback calls'] = function(test){
call_order.push(3);
callback(null, 'four');
},
- function(arg4){
+ function(/*arg4*/){
call_order.push(4);
arr[3] = function(){
call_order.push(4);
@@ -910,10 +1007,10 @@ exports['waterfall multiple callback calls'] = function(test){
}
];
async.waterfall(arr);
-};
+},
-exports['waterfall call in another context'] = function(test) {
- if (typeof process === 'undefined') {
+ 'call in another context': function(test) {
+ if (isBrowser()) {
// node only test
test.done();
return;
@@ -937,8 +1034,9 @@ exports['waterfall call in another context'] = function(test) {
}).toString() + "())";
vm.runInNewContext(fn, sandbox);
-};
+}
+};
exports['parallel'] = function(test){
var call_order = [];
@@ -963,7 +1061,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();
@@ -972,7 +1070,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();
});
@@ -987,7 +1085,7 @@ exports['parallel error'] = function(test){
callback('error2', 2);
}
],
- function(err, results){
+ function(err){
test.equals(err, 'error');
});
setTimeout(test.done, 100);
@@ -1014,6 +1112,42 @@ exports['parallel object'] = function(test){
});
};
+// Issue 10 on github: https://github.com/caolan/async/issues#issue/10
+exports['paralel falsy return values'] = function (test) {
+ function taskFalse(callback) {
+ 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) {
+ test.equal(results.length, 4);
+ test.strictEqual(results[0], false);
+ test.strictEqual(results[1], undefined);
+ test.strictEqual(results[2], undefined);
+ test.strictEqual(results[3], null);
+ test.done();
+ }
+ );
+};
+
+
exports['parallel limit'] = function(test){
var call_order = [];
async.parallelLimit([
@@ -1038,7 +1172,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();
@@ -1047,7 +1181,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();
});
@@ -1063,7 +1197,7 @@ exports['parallel limit error'] = function(test){
}
],
1,
- function(err, results){
+ function(err){
test.equals(err, 'error');
});
setTimeout(test.done, 100);
@@ -1091,7 +1225,7 @@ exports['parallel limit object'] = function(test){
};
exports['parallel call in another context'] = function(test) {
- if (typeof process === 'undefined') {
+ if (isBrowser()) {
// node only test
test.done();
return;
@@ -1116,8 +1250,44 @@ exports['parallel call in another context'] = function(test) {
vm.runInNewContext(fn, sandbox);
};
+exports['parallel does not continue replenishing after error'] = function (test) {
+ var started = 0;
+ var arr = [
+ funcToCall,
+ funcToCall,
+ funcToCall,
+ funcToCall,
+ funcToCall,
+ funcToCall,
+ funcToCall,
+ funcToCall,
+ funcToCall,
+ ];
+ var delay = 10;
+ var limit = 3;
+ var maxTime = 10 * arr.length;
+ function funcToCall(callback) {
+ started ++;
+ if (started === 3) {
+ return callback(new Error ("Test Error"));
+ }
+ setTimeout(function(){
+ callback();
+ }, delay);
+ }
+
+ async.parallelLimit(arr, limit, function(){});
+
+ setTimeout(function(){
+ test.equal(started, 3);
+ test.done();
+ }, maxTime);
+};
+
+
+exports['series'] = {
-exports['series'] = function(test){
+ 'series': function(test){
var call_order = [];
async.series([
function(callback){
@@ -1140,22 +1310,22 @@ 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();
});
-};
+},
-exports['series empty array'] = function(test){
+ 'empty array': function(test){
async.series([], function(err, results){
test.equals(err, null);
test.same(results, []);
test.done();
});
-};
+},
-exports['series error'] = function(test){
+ 'error': function(test){
test.expect(1);
async.series([
function(callback){
@@ -1166,20 +1336,20 @@ exports['series error'] = function(test){
callback('error2', 2);
}
],
- function(err, results){
+ function(err){
test.equals(err, 'error');
});
setTimeout(test.done, 100);
-};
+},
-exports['series no callback'] = function(test){
+ 'no callback': function(test){
async.series([
function(callback){callback();},
function(callback){callback(); test.done();},
]);
-};
+},
-exports['series object'] = function(test){
+ 'object': function(test){
var call_order = [];
async.series(getFunctionsObject(call_order), function(err, results){
test.equals(err, null);
@@ -1191,10 +1361,10 @@ exports['series object'] = function(test){
test.same(call_order, [1,2,3]);
test.done();
});
-};
+},
-exports['series call in another context'] = function(test) {
- if (typeof process === 'undefined') {
+ 'call in another context': function(test) {
+ if (isBrowser()) {
// node only test
test.done();
return;
@@ -1217,6 +1387,43 @@ exports['series call in another context'] = function(test) {
}).toString() + "())";
vm.runInNewContext(fn, sandbox);
+},
+
+ // Issue 10 on github: https://github.com/caolan/async/issues#issue/10
+ 'falsy return values': function (test) {
+ function taskFalse(callback) {
+ 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) {
+ test.equal(results.length, 4);
+ test.strictEqual(results[0], false);
+ test.strictEqual(results[1], undefined);
+ test.strictEqual(results[2], undefined);
+ test.strictEqual(results[3], null);
+ test.done();
+ }
+ );
+}
+
};
@@ -1279,6 +1486,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();
});
@@ -1302,11 +1510,28 @@ exports['each empty array'] = function(test){
test.ok(false, 'iterator should not be called');
callback();
}, function(err){
+ if (err) throw err;
test.ok(true, 'should call callback');
});
setTimeout(test.done, 25);
};
+
+exports['each empty array, with other property on the array'] = function(test){
+ test.expect(1);
+ var myArray = [];
+ myArray.myProp = "anything";
+ async.each(myArray, function(x, callback){
+ test.ok(false, 'iterator should not be called');
+ callback();
+ }, function(err){
+ if (err) throw err;
+ test.ok(true, 'should call callback');
+ });
+ setTimeout(test.done, 25);
+};
+
+
exports['each error'] = function(test){
test.expect(1);
async.each([1,2,3], function(x, callback){
@@ -1326,9 +1551,84 @@ 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 - instant resolver'] = function(test){
+ test.expect(1);
+ var args = [];
+ async.forEachOf({ a: 1, b: 2 }, function(x, k, cb) {
+ args.push(k, x);
+ cb();
+ }, function(){
+ // ensures done callback isn't called before all items iterated
+ 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) {
+ if (err) throw err;
+ test.ok(true, 'should call callback');
+ });
+ setTimeout(test.done, 25);
+};
+
+exports['forEachOf empty array'] = function(test){
+ test.expect(1);
+ async.forEachOf([], function(value, key, callback){
+ test.ok(false, 'iterator should not be called');
+ callback();
+ }, function(err) {
+ if (err) throw 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['eachOf alias'] = function(test){
+ test.equals(async.eachOf, async.forEachOf);
+ test.done();
+};
+
+exports['forEachOf with array'] = function(test){
+ var args = [];
+ async.forEachOf([ "a", "b" ], forEachOfIterator.bind(this, args), function(err){
+ if (err) throw 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();
});
@@ -1340,11 +1640,53 @@ exports['eachSeries empty array'] = function(test){
test.ok(false, 'iterator should not be called');
callback();
}, function(err){
+ if (err) throw err;
test.ok(true, 'should call callback');
});
setTimeout(test.done, 25);
};
+exports['eachSeries array modification'] = function(test) {
+ test.expect(1);
+ var arr = [1, 2, 3, 4];
+ async.eachSeries(arr, function (x, callback) {
+ async.setImmediate(callback);
+ }, function () {
+ test.ok(true, 'should call callback');
+ });
+
+ arr.pop();
+ arr.splice(0, 1);
+
+ setTimeout(test.done, 50);
+};
+
+// bug #782. Remove in next major release
+exports['eachSeries single item'] = function (test) {
+ test.expect(1);
+ var sync = true;
+ async.eachSeries([1], function (i, cb) {
+ cb(null);
+ }, function () {
+ test.ok(sync, "callback not called on same tick");
+ });
+ sync = false;
+ test.done();
+};
+
+// bug #782. Remove in next major release
+exports['eachSeries single item'] = function (test) {
+ test.expect(1);
+ var sync = true;
+ async.eachSeries([1], function (i, cb) {
+ cb(null);
+ }, function () {
+ test.ok(sync, "callback not called on same tick");
+ });
+ sync = false;
+ test.done();
+};
+
exports['eachSeries error'] = function(test){
test.expect(2);
var call_order = [];
@@ -1362,10 +1704,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 = [];
@@ -1376,6 +1714,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();
});
@@ -1387,6 +1726,7 @@ exports['eachLimit empty array'] = function(test){
test.ok(false, 'iterator should not be called');
callback();
}, function(err){
+ if (err) throw err;
test.ok(true, 'should call callback');
});
setTimeout(test.done, 25);
@@ -1396,6 +1736,7 @@ exports['eachLimit limit exceeds size'] = function(test){
var args = [];
var arr = [0,1,2,3,4,5,6,7,8,9];
async.eachLimit(arr, 20, eachIterator.bind(this, args), function(err){
+ if (err) throw err;
test.same(args, arr);
test.done();
});
@@ -1405,6 +1746,7 @@ exports['eachLimit limit equal size'] = function(test){
var args = [];
var arr = [0,1,2,3,4,5,6,7,8,9];
async.eachLimit(arr, 10, eachIterator.bind(this, args), function(err){
+ if (err) throw err;
test.same(args, arr);
test.done();
});
@@ -1416,6 +1758,7 @@ exports['eachLimit zero limit'] = function(test){
test.ok(false, 'iterator should not be called');
callback();
}, function(err){
+ if (err) throw err;
test.ok(true, 'should call callback');
});
setTimeout(test.done, 25);
@@ -1449,26 +1792,218 @@ exports['eachLimit synchronous'] = function(test){
args.push(x);
callback();
}, function(err){
+ if (err) throw err;
test.same(args, arr);
test.done();
});
};
+
+exports['eachLimit does not continue replenishing after error'] = function (test) {
+ var started = 0;
+ var arr = [0,1,2,3,4,5,6,7,8,9];
+ var delay = 10;
+ var limit = 3;
+ var maxTime = 10 * arr.length;
+
+ async.eachLimit(arr, limit, function(x, callback) {
+ started ++;
+ if (started === 3) {
+ return callback(new Error ("Test Error"));
+ }
+ setTimeout(function(){
+ callback();
+ }, delay);
+ }, function(){});
+
+ setTimeout(function(){
+ test.equal(started, 3);
+ test.done();
+ }, maxTime);
+};
+
+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){
+ if (err) throw 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){
+ if (err) throw err;
+ test.same(args, [ 0, "a", 1, "b" ]);
+ test.done();
+ });
+};
+
+
+exports['eachOfLimit alias'] = function(test){
+ test.equals(async.eachOfLimit, async.forEachOfLimit);
+ test.done();
+};
+
+
+exports['eachOfSeries alias'] = function(test){
+ test.equals(async.eachOfSeries, async.forEachOfSeries);
+ test.done();
+};
+
exports['forEachLimit alias'] = function (test) {
test.strictEqual(async.eachLimit, async.forEachLimit);
test.done();
};
-exports['map'] = function(test){
+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){
+ if (err) throw 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){
+ if (err) throw 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){
+ if (err) throw 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){
+ if (err) throw 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){
+ if (err) throw 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) {
+ if (err) throw err;
+ test.same(args, [ 0, "a", 1, "b" ]);
+ test.done();
+ });
+};
+
+exports['map'] = {
+
+ 'basic': 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();
});
-};
+},
-exports['map original untouched'] = function(test){
+ 'map original untouched': function(test){
var a = [1,2,3];
async.map(a, function(x, callback){
callback(null, x*2);
@@ -1477,9 +2012,9 @@ exports['map original untouched'] = function(test){
test.same(a, [1,2,3]);
test.done();
});
-};
+},
-exports['map without main callback'] = function(test){
+ 'map without main callback': function(test){
var a = [1,2,3];
var r = [];
async.map(a, function(x, callback){
@@ -1490,77 +2025,133 @@ exports['map without main callback'] = function(test){
test.done();
}
});
-};
+},
-exports['map error'] = function(test){
+ 'map error': function(test){
test.expect(1);
async.map([1,2,3], function(x, callback){
callback('error');
- }, function(err, results){
+ }, function(err){
test.equals(err, 'error');
});
setTimeout(test.done, 50);
-};
+},
+
+ 'map undefined array': function(test){
+ test.expect(2);
+ async.map(undefined, function(x, callback){
+ callback();
+ }, function(err, result){
+ test.equals(err, null);
+ test.same(result, []);
+ });
+ setTimeout(test.done, 50);
+},
+
+ 'map object': function (test) {
+ async.map({a: 1, b: 2, c: 3}, function (val, callback) {
+ callback(null, val * 2);
+ }, function (err, result) {
+ if (err) throw err;
+ test.equals(Object.prototype.toString.call(result), '[object Object]');
+ test.same(result, {a: 2, b: 4, c: 6});
+ test.done();
+ });
+},
-exports['mapSeries'] = function(test){
+ '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();
});
-};
+},
-exports['mapSeries error'] = function(test){
+ 'mapSeries error': function(test){
test.expect(1);
async.mapSeries([1,2,3], function(x, callback){
callback('error');
- }, function(err, results){
+ }, function(err){
test.equals(err, 'error');
});
setTimeout(test.done, 50);
-};
+},
+ 'mapSeries undefined array': function(test){
+ test.expect(2);
+ async.mapSeries(undefined, function(x, callback){
+ callback();
+ }, function(err, result){
+ test.equals(err, null);
+ test.same(result, []);
+ });
+ setTimeout(test.done, 50);
+},
+
+ 'mapSeries object': function (test) {
+ async.mapSeries({a: 1, b: 2, c: 3}, function (val, callback) {
+ callback(null, val * 2);
+ }, function (err, result) {
+ if (err) throw err;
+ test.same(result, {a: 2, b: 4, c: 6});
+ test.done();
+ });
+},
-exports['mapLimit'] = function(test){
+ '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();
});
-};
+},
-exports['mapLimit empty array'] = function(test){
+ 'mapLimit empty array': function(test){
test.expect(1);
async.mapLimit([], 2, function(x, callback){
test.ok(false, 'iterator should not be called');
callback();
}, function(err){
+ if (err) throw err;
test.ok(true, 'should call callback');
});
setTimeout(test.done, 25);
-};
+},
+
+ 'mapLimit undefined array': function(test){
+ test.expect(2);
+ async.mapLimit(undefined, 2, function(x, callback){
+ callback();
+ }, function(err, result){
+ test.equals(err, null);
+ test.same(result, []);
+ });
+ setTimeout(test.done, 50);
+},
-exports['mapLimit limit exceeds size'] = function(test){
+ 'mapLimit limit exceeds size': function(test){
var call_order = [];
async.mapLimit([0,1,2,3,4,5,6,7,8,9], 20, mapIterator.bind(this, call_order), function(err, results){
test.same(call_order, [0,1,2,3,4,5,6,7,8,9]);
test.same(results, [0,2,4,6,8,10,12,14,16,18]);
test.done();
});
-};
+},
-exports['mapLimit limit equal size'] = function(test){
+ 'mapLimit limit equal size': function(test){
var call_order = [];
async.mapLimit([0,1,2,3,4,5,6,7,8,9], 10, mapIterator.bind(this, call_order), function(err, results){
test.same(call_order, [0,1,2,3,4,5,6,7,8,9]);
test.same(results, [0,2,4,6,8,10,12,14,16,18]);
test.done();
});
-};
+},
-exports['mapLimit zero limit'] = function(test){
+ 'mapLimit zero limit': function(test){
test.expect(2);
async.mapLimit([0,1,2,3,4,5], 0, function(x, callback){
test.ok(false, 'iterator should not be called');
@@ -1570,9 +2161,9 @@ exports['mapLimit zero limit'] = function(test){
test.ok(true, 'should call callback');
});
setTimeout(test.done, 25);
-};
+},
-exports['mapLimit error'] = function(test){
+ 'mapLimit error': function(test){
test.expect(2);
var arr = [0,1,2,3,4,5,6,7,8,9];
var call_order = [];
@@ -1587,6 +2178,31 @@ exports['mapLimit error'] = function(test){
test.equals(err, 'error');
});
setTimeout(test.done, 25);
+},
+
+ 'mapLimit does not continue replenishing after error': function (test) {
+ var started = 0;
+ var arr = [0,1,2,3,4,5,6,7,8,9];
+ var delay = 10;
+ var limit = 3;
+ var maxTime = 10 * arr.length;
+
+ async.mapLimit(arr, limit, function(x, callback) {
+ started ++;
+ if (started === 3) {
+ return callback(new Error ("Test Error"));
+ }
+ setTimeout(function(){
+ callback();
+ }, delay);
+ }, function(){});
+
+ setTimeout(function(){
+ test.equal(started, 3);
+ test.done();
+ }, maxTime);
+}
+
};
@@ -1596,6 +2212,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();
@@ -1604,7 +2221,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();
@@ -1615,7 +2232,7 @@ exports['reduce error'] = function(test){
test.expect(1);
async.reduce([1,2,3], 0, function(a, x, callback){
callback('error');
- }, function(err, result){
+ }, function(err){
test.equals(err, 'error');
});
setTimeout(test.done, 50);
@@ -1650,6 +2267,59 @@ exports['foldr alias'] = function(test){
test.done();
};
+exports['transform implictly determines memo if not provided'] = function(test){
+ async.transform([1,2,3], function(memo, x, v, callback){
+ memo.push(x + 1);
+ callback();
+ }, function(err, result){
+ test.same(result, [2, 3, 4]);
+ test.done();
+ });
+};
+
+exports['transform async with object memo'] = function(test){
+ test.expect(2);
+
+ async.transform([1,3,2], {}, function(memo, v, k, callback){
+ setTimeout(function() {
+ memo[k] = v;
+ callback();
+ });
+ }, function(err, result) {
+ test.equals(err, null);
+ test.same(result, {
+ 0: 1,
+ 1: 3,
+ 2: 2
+ });
+ test.done();
+ });
+};
+
+exports['transform iterating object'] = function(test){
+ test.expect(2);
+
+ async.transform({a: 1, b: 3, c: 2}, function(memo, v, k, callback){
+ setTimeout(function() {
+ memo[k] = v + 1;
+ callback();
+ });
+ }, function(err, result) {
+ test.equals(err, null);
+ test.same(result, {a: 2, b: 4, c: 3});
+ test.done();
+ });
+};
+
+exports['transform error'] = function(test){
+ async.transform([1,2,3], function(a, v, k, callback){
+ callback('error');
+ }, function(err){
+ test.equals(err, 'error');
+ test.done();
+ });
+};
+
exports['filter'] = function(test){
async.filter([3,1,2], filterIterator, function(results){
test.same(results, [3,1]);
@@ -1686,6 +2356,7 @@ exports['selectSeries alias'] = function(test){
};
exports['reject'] = function(test){
+ test.expect(1);
async.reject([3,1,2], filterIterator, function(results){
test.same(results, [2]);
test.done();
@@ -1693,6 +2364,7 @@ exports['reject'] = function(test){
};
exports['reject original untouched'] = function(test){
+ test.expect(2);
var a = [3,1,2];
async.reject(a, function(x, callback){
callback(x % 2);
@@ -1704,13 +2376,48 @@ exports['reject original untouched'] = function(test){
};
exports['rejectSeries'] = function(test){
+ test.expect(1);
async.rejectSeries([3,1,2], filterIterator, function(results){
test.same(results, [2]);
test.done();
});
};
+function testLimit(test, arr, limitFunc, limit, iter, done) {
+ var args = [];
+
+ limitFunc(arr, limit, function(x) {
+ args.push(x);
+ iter.apply(this, arguments);
+ }, function() {
+ test.same(args, arr);
+ if (done) done.apply(this, arguments);
+ else test.done();
+ });
+}
+
+exports['rejectLimit'] = function(test) {
+ test.expect(2);
+ testLimit(test, [5, 4, 3, 2, 1], async.rejectLimit, 2, function(v, next) {
+ next(v % 2);
+ }, function(x) {
+ test.same(x, [4, 2]);
+ test.done();
+ });
+};
+
+exports['filterLimit'] = function(test) {
+ test.expect(2);
+ testLimit(test, [5, 4, 3, 2, 1], async.filterLimit, 2, function(v, next) {
+ next(v % 2);
+ }, function(x) {
+ test.same(x, [5, 3, 1]);
+ test.done();
+ });
+};
+
exports['some true'] = function(test){
+ test.expect(1);
async.some([3,1,2], function(x, callback){
setTimeout(function(){callback(x === 1);}, 0);
}, function(result){
@@ -1720,6 +2427,7 @@ exports['some true'] = function(test){
};
exports['some false'] = function(test){
+ test.expect(1);
async.some([3,1,2], function(x, callback){
setTimeout(function(){callback(x === 10);}, 0);
}, function(result){
@@ -1729,13 +2437,14 @@ exports['some false'] = function(test){
};
exports['some early return'] = function(test){
+ test.expect(1);
var call_order = [];
async.some([1,2,3], function(x, callback){
setTimeout(function(){
call_order.push(x);
callback(x === 1);
}, x*25);
- }, function(result){
+ }, function(){
call_order.push('callback');
});
setTimeout(function(){
@@ -1744,12 +2453,76 @@ exports['some early return'] = function(test){
}, 100);
};
+exports['someLimit true'] = function(test){
+ async.someLimit([3,1,2], 2, function(x, callback){
+ setTimeout(function(){callback(x === 2);}, 0);
+ }, function(result){
+ test.equals(result, true);
+ test.done();
+ });
+};
+
+exports['someLimit false'] = function(test){
+ async.someLimit([3,1,2], 2, function(x, callback){
+ setTimeout(function(){callback(x === 10);}, 0);
+ }, function(result){
+ test.equals(result, false);
+ test.done();
+ });
+};
+
+exports['every true'] = function(test){
+ async.everyLimit([3,1,2], 1, function(x, callback){
+ setTimeout(function(){callback(x > 1);}, 0);
+ }, function(result){
+ test.equals(result, true);
+ test.done();
+ });
+};
+
+exports['everyLimit false'] = function(test){
+ async.everyLimit([3,1,2], 2, function(x, callback){
+ setTimeout(function(){callback(x === 2);}, 0);
+ }, function(result){
+ test.equals(result, false);
+ test.done();
+ });
+};
+
+exports['everyLimit short-circuit'] = function(test){
+ test.expect(2);
+ var calls = 0;
+ async.everyLimit([3,1,2], 1, function(x, callback){
+ calls++;
+ callback(x === 1);
+ }, function(result){
+ test.equals(result, false);
+ test.equals(calls, 1);
+ test.done();
+ });
+};
+
+
+exports['someLimit short-circuit'] = function(test){
+ test.expect(2);
+ var calls = 0;
+ async.someLimit([3,1,2], 1, function(x, callback){
+ calls++;
+ callback(x === 1);
+ }, function(result){
+ test.equals(result, true);
+ test.equals(calls, 2);
+ test.done();
+ });
+};
+
exports['any alias'] = function(test){
test.equals(async.any, async.some);
test.done();
};
exports['every true'] = function(test){
+ test.expect(1);
async.every([1,2,3], function(x, callback){
setTimeout(function(){callback(true);}, 0);
}, function(result){
@@ -1759,6 +2532,7 @@ exports['every true'] = function(test){
};
exports['every false'] = function(test){
+ test.expect(1);
async.every([1,2,3], function(x, callback){
setTimeout(function(){callback(x % 2);}, 0);
}, function(result){
@@ -1768,13 +2542,14 @@ exports['every false'] = function(test){
};
exports['every early return'] = function(test){
+ test.expect(1);
var call_order = [];
async.every([1,2,3], function(x, callback){
setTimeout(function(){
call_order.push(x);
callback(x === 1);
}, x*25);
- }, function(result){
+ }, function(){
call_order.push('callback');
});
setTimeout(function(){
@@ -1789,6 +2564,7 @@ exports['all alias'] = function(test){
};
exports['detect'] = function(test){
+ test.expect(2);
var call_order = [];
async.detect([3,2,1], detectIterator.bind(this, call_order), function(result){
call_order.push('callback');
@@ -1801,6 +2577,7 @@ exports['detect'] = function(test){
};
exports['detect - mulitple matches'] = function(test){
+ test.expect(2);
var call_order = [];
async.detect([3,2,2,1,2], detectIterator.bind(this, call_order), function(result){
call_order.push('callback');
@@ -1813,6 +2590,7 @@ exports['detect - mulitple matches'] = function(test){
};
exports['detectSeries'] = function(test){
+ test.expect(2);
var call_order = [];
async.detectSeries([3,2,1], detectIterator.bind(this, call_order), function(result){
call_order.push('callback');
@@ -1825,6 +2603,7 @@ exports['detectSeries'] = function(test){
};
exports['detectSeries - multiple matches'] = function(test){
+ test.expect(2);
var call_order = [];
async.detectSeries([3,2,2,1,2], detectIterator.bind(this, call_order), function(result){
call_order.push('callback');
@@ -1836,16 +2615,69 @@ exports['detectSeries - multiple matches'] = function(test){
}, 200);
};
+exports['detectSeries - ensure stop'] = function (test) {
+ test.expect(1);
+ 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['detectLimit'] = function(test){
+ test.expect(2);
+ var call_order = [];
+ async.detectLimit([3, 2, 1], 2, detectIterator.bind(this, call_order), function(result) {
+ call_order.push('callback');
+ test.equals(result, 2);
+ });
+ setTimeout(function() {
+ test.same(call_order, [2, 'callback', 3]);
+ test.done();
+ }, 100);
+};
+
+exports['detectLimit - multiple matches'] = function(test){
+ test.expect(2);
+ var call_order = [];
+ async.detectLimit([3,2,2,1,2], 2, detectIterator.bind(this, call_order), function(result){
+ call_order.push('callback');
+ test.equals(result, 2);
+ });
+ setTimeout(function(){
+ test.same(call_order, [2, 'callback', 3]);
+ test.done();
+ }, 100);
+};
+
+exports['detectLimit - ensure stop'] = function (test) {
+ test.expect(1);
+ async.detectLimit([1, 2, 3, 4, 5], 2, function (num, cb) {
+ if (num > 4) throw new Error("detectLimit did not stop iterating");
+ cb(num === 3);
+ }, function (result) {
+ test.equals(result, 3);
+ test.done();
+ });
+};
+
exports['sortBy'] = function(test){
+ test.expect(2);
+
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();
});
};
exports['sortBy inverted'] = function(test){
+ test.expect(1);
+
async.sortBy([{a:1},{a:15},{a:6}], function(x, callback){
setTimeout(function(){callback(null, x.a*-1);}, 0);
}, function(err, result){
@@ -1854,10 +2686,23 @@ exports['sortBy inverted'] = function(test){
});
};
+exports['sortBy error'] = function(test){
+ test.expect(1);
+ var error = new Error('asdas');
+ async.sortBy([{a:1},{a:15},{a:6}], function(x, callback){
+ async.setImmediate(function(){
+ callback(error);
+ });
+ }, function(err){
+ test.equal(err, error);
+ test.done();
+ });
+};
+
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);
@@ -1865,7 +2710,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();
@@ -1903,6 +2748,7 @@ var console_fn_tests = function(name){
};
exports[name + ' with multiple result params'] = function(test){
+ test.expect(1);
var fn = function(callback){callback(null,'one','two','three');};
var _console_fn = console[name];
var called_with = [];
@@ -1933,18 +2779,21 @@ var console_fn_tests = function(name){
};
+exports['times'] = {
-exports['times'] = function(test) {
- var indices = []
- async.times(5, function(n, next) {
- next(null, n)
- }, function(err, results) {
- test.same(results, [0,1,2,3,4])
- test.done()
- })
-}
+ 'times': function(test) {
+ test.expect(2);
+ async.times(5, function(n, next) {
+ next(null, n);
+ }, function(err, results) {
+ test.ok(err === null, err + " passed instead of 'null'");
+ test.same(results, [0,1,2,3,4]);
+ test.done();
+ });
+},
-exports['times'] = function(test){
+ 'times 3': function(test){
+ test.expect(1);
var args = [];
async.times(3, function(n, callback){
setTimeout(function(){
@@ -1952,23 +2801,25 @@ exports['times'] = function(test){
callback();
}, n * 25);
}, function(err){
+ if (err) throw err;
test.same(args, [0,1,2]);
test.done();
});
-};
+},
-exports['times 0'] = function(test){
+ 'times 0': function(test){
test.expect(1);
async.times(0, function(n, callback){
test.ok(false, 'iterator should not be called');
callback();
}, function(err){
+ if (err) throw err;
test.ok(true, 'should call callback');
});
setTimeout(test.done, 25);
-};
+},
-exports['times error'] = function(test){
+ 'times error': function(test){
test.expect(1);
async.times(3, function(n, callback){
callback('error');
@@ -1976,9 +2827,10 @@ exports['times error'] = function(test){
test.equals(err, 'error');
});
setTimeout(test.done, 50);
-};
+},
-exports['timesSeries'] = function(test){
+ 'timesSeries': function(test){
+ test.expect(2);
var call_order = [];
async.timesSeries(5, function(n, callback){
setTimeout(function(){
@@ -1990,16 +2842,37 @@ exports['timesSeries'] = function(test){
test.same(results, [0,1,2,3,4]);
test.done();
});
-};
+},
-exports['timesSeries error'] = function(test){
+ 'timesSeries error': function(test){
test.expect(1);
async.timesSeries(5, function(n, callback){
callback('error');
- }, function(err, results){
+ }, function(err){
test.equals(err, 'error');
});
setTimeout(test.done, 50);
+},
+
+ 'timesLimit': function(test){
+ test.expect(7);
+
+ var limit = 2;
+ var running = 0;
+ async.timesLimit(5, limit, function (i, next) {
+ running++;
+ test.ok(running <= limit && running > 0, running);
+ setTimeout(function () {
+ running--;
+ next(null, i * 2);
+ }, (3 - i) * 10);
+ }, function(err, results){
+ test.ok(err === null, err + " passed instead of 'null'");
+ test.same(results, [0, 2, 4, 6, 8]);
+ test.done();
+ });
+}
+
};
console_fn_tests('log');
@@ -2009,6 +2882,7 @@ console_fn_tests('warn');
console_fn_tests('error');*/
exports['nextTick'] = function(test){
+ test.expect(1);
var call_order = [];
async.nextTick(function(){call_order.push('two');});
call_order.push('one');
@@ -2019,7 +2893,7 @@ exports['nextTick'] = function(test){
};
exports['nextTick in the browser'] = function(test){
- if (typeof process !== 'undefined') {
+ if (!isBrowser()) {
// skip this test in node
return test.done();
}
@@ -2030,28 +2904,23 @@ 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);
};
exports['noConflict - node only'] = function(test){
- if (typeof process !== 'undefined') {
+ if (!isBrowser()) {
// 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
);
@@ -2072,6 +2941,7 @@ exports['noConflict - node only'] = function(test){
};
exports['concat'] = function(test){
+ test.expect(3);
var call_order = [];
var iterator = function (x, cb) {
setTimeout(function(){
@@ -2087,22 +2957,24 @@ 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();
});
};
exports['concat error'] = function(test){
+ test.expect(1);
var iterator = function (x, cb) {
cb(new Error('test error'));
};
- async.concat([1,2,3], iterator, function(err, results){
+ async.concat([1,2,3], iterator, function(err){
test.ok(err);
test.done();
});
};
exports['concatSeries'] = function(test){
+ test.expect(3);
var call_order = [];
var iterator = function (x, cb) {
setTimeout(function(){
@@ -2118,14 +2990,15 @@ 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();
});
};
exports['until'] = function (test) {
- var call_order = [];
+ test.expect(4);
+ var call_order = [];
var count = 0;
async.until(
function () {
@@ -2135,9 +3008,11 @@ exports['until'] = function (test) {
function (cb) {
call_order.push(['iterator', count]);
count++;
- cb();
+ cb(null, count);
},
- function (err) {
+ function (err, result) {
+ test.ok(err === null, err + " passed instead of 'null'");
+ test.equals(result, 5, 'last result passed through');
test.same(call_order, [
['test', 0],
['iterator', 0], ['test', 1],
@@ -2153,20 +3028,23 @@ exports['until'] = function (test) {
};
exports['doUntil'] = function (test) {
+ test.expect(4);
+
var call_order = [];
var count = 0;
async.doUntil(
function (cb) {
- debugger
call_order.push(['iterator', count]);
count++;
- cb();
+ cb(null, count);
},
function () {
call_order.push(['test', count]);
return (count == 5);
},
- function (err) {
+ function (err, result) {
+ test.ok(err === null, err + " passed instead of 'null'");
+ test.equals(result, 5, 'last result passed through');
test.same(call_order, [
['iterator', 0], ['test', 1],
['iterator', 1], ['test', 2],
@@ -2181,11 +3059,12 @@ exports['doUntil'] = function (test) {
};
exports['doUntil callback params'] = function (test) {
+ test.expect(3);
+
var call_order = [];
var count = 0;
async.doUntil(
function (cb) {
- debugger
call_order.push(['iterator', count]);
count++;
cb(null, count);
@@ -2194,7 +3073,9 @@ exports['doUntil callback params'] = function (test) {
call_order.push(['test', c]);
return (c == 5);
},
- function (err) {
+ function (err, result) {
+ if (err) throw err;
+ test.equals(result, 5, 'last result passed through');
test.same(call_order, [
['iterator', 0], ['test', 1],
['iterator', 1], ['test', 2],
@@ -2209,6 +3090,8 @@ exports['doUntil callback params'] = function (test) {
};
exports['whilst'] = function (test) {
+ test.expect(4);
+
var call_order = [];
var count = 0;
@@ -2220,9 +3103,11 @@ exports['whilst'] = function (test) {
function (cb) {
call_order.push(['iterator', count]);
count++;
- cb();
+ cb(null, count);
},
- function (err) {
+ function (err, result) {
+ test.ok(err === null, err + " passed instead of 'null'");
+ test.equals(result, 5, 'last result passed through');
test.same(call_order, [
['test', 0],
['iterator', 0], ['test', 1],
@@ -2238,6 +3123,7 @@ exports['whilst'] = function (test) {
};
exports['doWhilst'] = function (test) {
+ test.expect(4);
var call_order = [];
var count = 0;
@@ -2245,14 +3131,15 @@ exports['doWhilst'] = function (test) {
function (cb) {
call_order.push(['iterator', count]);
count++;
- cb();
+ cb(null, count);
},
function () {
call_order.push(['test', count]);
return (count < 5);
},
- function (err) {
- debugger
+ function (err, result) {
+ test.ok(err === null, err + " passed instead of 'null'");
+ test.equals(result, 5, 'last result passed through');
test.same(call_order, [
['iterator', 0], ['test', 1],
['iterator', 1], ['test', 2],
@@ -2267,8 +3154,8 @@ exports['doWhilst'] = function (test) {
};
exports['doWhilst callback params'] = function (test) {
+ test.expect(3);
var call_order = [];
-
var count = 0;
async.doWhilst(
function (cb) {
@@ -2280,8 +3167,9 @@ exports['doWhilst callback params'] = function (test) {
call_order.push(['test', c]);
return (c < 5);
},
- function (err) {
- debugger
+ function (err, result) {
+ if (err) throw err;
+ test.equals(result, 5, 'last result passed through');
test.same(call_order, [
['iterator', 0], ['test', 1],
['iterator', 1], ['test', 2],
@@ -2295,7 +3183,133 @@ exports['doWhilst callback params'] = function (test) {
);
};
-exports['queue'] = function (test) {
+exports['doWhilst - error'] = function (test) {
+ test.expect(1);
+ var error = new Error('asdas');
+
+ async.doWhilst(
+ function (cb) {
+ cb(error);
+ },
+ function () {},
+ function (err) {
+ test.equal(err, error);
+ test.done();
+ }
+ );
+};
+
+exports['during'] = function (test) {
+ var call_order = [];
+
+ var count = 0;
+ async.during(
+ function (cb) {
+ call_order.push(['test', count]);
+ cb(null, count < 5);
+ },
+ function (cb) {
+ call_order.push(['iterator', count]);
+ count++;
+ cb();
+ },
+ function (err) {
+ test.ok(err === null, err + " passed instead of 'null'");
+ test.same(call_order, [
+ ['test', 0],
+ ['iterator', 0], ['test', 1],
+ ['iterator', 1], ['test', 2],
+ ['iterator', 2], ['test', 3],
+ ['iterator', 3], ['test', 4],
+ ['iterator', 4], ['test', 5],
+ ]);
+ test.equals(count, 5);
+ test.done();
+ }
+ );
+};
+
+exports['doDuring'] = function (test) {
+ var call_order = [];
+
+ var count = 0;
+ async.doDuring(
+ function (cb) {
+ call_order.push(['iterator', count]);
+ count++;
+ cb();
+ },
+ function (cb) {
+ call_order.push(['test', count]);
+ cb(null, 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],
+ ['iterator', 2], ['test', 3],
+ ['iterator', 3], ['test', 4],
+ ['iterator', 4], ['test', 5],
+ ]);
+ test.equals(count, 5);
+ test.done();
+ }
+ );
+};
+
+exports['doDuring - error test'] = function (test) {
+ test.expect(1);
+ var error = new Error('asdas');
+
+ async.doDuring(
+ function (cb) {
+ cb(error);
+ },
+ function () {},
+ function (err) {
+ test.equal(err, error);
+ test.done();
+ }
+ );
+};
+
+exports['doDuring - error iterator'] = function (test) {
+ test.expect(1);
+ var error = new Error('asdas');
+
+ async.doDuring(
+ function (cb) {
+ cb(null);
+ },
+ function (cb) {
+ cb(error);
+ },
+ function (err) {
+ test.equal(err, error);
+ test.done();
+ }
+ );
+};
+
+exports['whilst optional callback'] = function (test) {
+ var counter = 0;
+ async.whilst(
+ function () { return counter < 2; },
+ function (cb) {
+ counter++;
+ cb();
+ }
+ );
+ test.equal(counter, 2);
+ test.done();
+};
+
+exports['queue'] = {
+
+ 'queue': function (test) {
+ test.expect(17);
+
var call_order = [],
delays = [160,80,240,80];
@@ -2348,9 +3362,10 @@ exports['queue'] = function (test) {
test.equal(q.length(), 0);
test.done();
};
-};
+},
-exports['queue default concurrency'] = function (test) {
+ 'default concurrency': function (test) {
+ test.expect(17);
var call_order = [],
delays = [160,80,240,80];
@@ -2401,9 +3416,20 @@ exports['queue default concurrency'] = function (test) {
test.equal(q.length(), 0);
test.done();
};
-};
+},
+
+ 'zero concurrency': function(test){
+ test.expect(1);
+ test.throws(function () {
+ async.queue(function (task, callback) {
+ callback(null, task);
+ }, 0);
+ });
+ test.done();
+},
-exports['queue error propagation'] = function(test){
+ 'error propagation': function(test){
+ test.expect(1);
var results = [];
var q = async.queue(function (task, callback) {
@@ -2432,64 +3458,46 @@ exports['queue error propagation'] = function(test){
results.push('foo');
});
-};
+},
-exports['queue changing concurrency'] = function (test) {
- var call_order = [],
- delays = [40,20,60,20];
+ // The original queue implementation allowed the concurrency to be changed only
+ // on the same event loop during which a task was added to the queue. This
+ // test attempts to be a more robust test.
+ // Start with a concurrency of 1. Wait until a leter event loop and change
+ // the concurrency to 2. Wait again for a later loop then verify the concurrency.
+ // Repeat that one more time by chaning the concurrency to 5.
+ 'changing concurrency': function (test) {
+ test.expect(3);
- // worker1: --1-2---3-4
- // order of completion: 1,2,3,4
+ var q = async.queue(function(task, callback){
+ setTimeout(function(){
+ callback();
+ }, 100);
+ }, 1);
- var q = async.queue(function (task, callback) {
- setTimeout(function () {
- call_order.push('process ' + task);
- callback('error', 'arg');
- }, delays.splice(0,1)[0]);
- }, 2);
+ for(var i = 0; i < 50; i++){
+ q.push('');
+ }
- q.push(1, function (err, arg) {
- test.equal(err, 'error');
- test.equal(arg, 'arg');
- test.equal(q.length(), 3);
- call_order.push('callback ' + 1);
- });
- q.push(2, function (err, arg) {
- test.equal(err, 'error');
- test.equal(arg, 'arg');
- test.equal(q.length(), 2);
- call_order.push('callback ' + 2);
- });
- q.push(3, function (err, arg) {
- test.equal(err, 'error');
- test.equal(arg, 'arg');
- test.equal(q.length(), 1);
- call_order.push('callback ' + 3);
- });
- q.push(4, function (err, arg) {
- test.equal(err, 'error');
- test.equal(arg, 'arg');
- test.equal(q.length(), 0);
- call_order.push('callback ' + 4);
- });
- test.equal(q.length(), 4);
- test.equal(q.concurrency, 2);
- q.concurrency = 1;
+ q.drain = function(){
+ test.done();
+ };
- setTimeout(function () {
- test.same(call_order, [
- 'process 1', 'callback 1',
- 'process 2', 'callback 2',
- 'process 3', 'callback 3',
- 'process 4', 'callback 4'
- ]);
+ setTimeout(function(){
test.equal(q.concurrency, 1);
- test.equal(q.length(), 0);
- test.done();
- }, 250);
-};
+ q.concurrency = 2;
+ setTimeout(function(){
+ test.equal(q.running(), 2);
+ q.concurrency = 5;
+ setTimeout(function(){
+ test.equal(q.running(), 5);
+ }, 500);
+ }, 500);
+ }, 500);
+},
-exports['queue push without callback'] = function (test) {
+ 'push without callback': function (test) {
+ test.expect(1);
var call_order = [],
delays = [160,80,240,80];
@@ -2518,14 +3526,24 @@ exports['queue push without callback'] = function (test) {
]);
test.done();
}, 800);
-};
+},
-exports['queue unshift'] = function (test) {
+ 'push with non-function': function (test) {
+ test.expect(1);
+ var q = async.queue(function () {}, 1);
+ test.throws(function () {
+ q.push({}, 1);
+ });
+ test.done();
+},
+
+ 'unshift': function (test) {
+ test.expect(1);
var queue_order = [];
var q = async.queue(function (task, callback) {
- queue_order.push(task);
- callback();
+ queue_order.push(task);
+ callback();
}, 1);
q.unshift(4);
@@ -2537,9 +3555,10 @@ exports['queue unshift'] = function (test) {
test.same(queue_order, [ 1, 2, 3, 4 ]);
test.done();
}, 100);
-};
+},
-exports['queue too many callbacks'] = function (test) {
+ 'too many callbacks': function (test) {
+ test.expect(1);
var q = async.queue(function (task, callback) {
callback();
test.throws(function() {
@@ -2549,9 +3568,10 @@ exports['queue too many callbacks'] = function (test) {
}, 2);
q.push(1);
-};
+},
-exports['queue bulk task'] = function (test) {
+ 'bulk task': function (test) {
+ test.expect(9);
var call_order = [],
delays = [160,80,240,80];
@@ -2585,17 +3605,18 @@ exports['queue bulk task'] = function (test) {
test.equal(q.length(), 0);
test.done();
}, 800);
-};
+},
-exports['queue idle'] = function(test) {
+ 'idle': function(test) {
+ test.expect(7);
var q = async.queue(function (task, callback) {
- // Queue is busy when workers are running
- test.equal(q.idle(), false)
- callback();
+ // Queue is busy when workers are running
+ 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);
@@ -2603,16 +3624,17 @@ 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) {
+ 'pause': function(test) {
+ test.expect(3);
var call_order = [],
task_timeout = 100,
pause_timeout = 300,
@@ -2620,8 +3642,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) {
@@ -2661,9 +3685,40 @@ exports['queue pause'] = function(test) {
]);
test.done();
}, 800);
-}
+},
+
+ 'pause in worker with concurrency': function(test) {
+ test.expect(1);
+ var call_order = [];
+ var q = async.queue(function (task, callback) {
+ if (task.isLongRunning) {
+ q.pause();
+ setTimeout(function () {
+ call_order.push(task.id);
+ q.resume();
+ callback();
+ }, 500);
+ }
+ else {
+ call_order.push(task.id);
+ callback();
+ }
+ }, 10);
+
+ q.push({ id: 1, isLongRunning: true});
+ q.push({ id: 2 });
+ q.push({ id: 3 });
+ q.push({ id: 4 });
+ q.push({ id: 5 });
+
+ setTimeout(function () {
+ test.same(call_order, [1, 2, 3, 4, 5]);
+ test.done();
+ }, 1000);
+ },
-exports['queue pause with concurrency'] = function(test) {
+ 'pause with concurrency': function(test) {
+ test.expect(4);
var call_order = [],
task_timeout = 100,
pause_timeout = 50,
@@ -2671,8 +3726,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) {
@@ -2696,6 +3753,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',
@@ -2706,9 +3767,36 @@ exports['queue pause with concurrency'] = function(test) {
]);
test.done();
}, 800);
-}
+},
+
+ 'start paused': function (test) {
+ test.expect(2);
+ var q = async.queue(function (task, callback) {
+ setTimeout(function () {
+ callback();
+ }, 40);
+ }, 2);
+ q.pause();
+
+ q.push([1, 2, 3]);
+
+ setTimeout(function () {
+ q.resume();
+ }, 5);
+
+ setTimeout(function () {
+ test.equal(q.tasks.length, 1);
+ test.equal(q.running(), 2);
+ q.resume();
+ }, 15);
+
+ q.drain = function () {
+ test.done();
+ };
+},
-exports['queue kill'] = function (test) {
+ 'kill': function (test) {
+ test.expect(1);
var q = async.queue(function (task, callback) {
setTimeout(function () {
test.ok(false, "Function should never be called");
@@ -2717,26 +3805,136 @@ exports['queue kill'] = function (test) {
}, 1);
q.drain = function() {
test.ok(false, "Function should never be called");
- }
+ };
q.push(0);
q.kill();
setTimeout(function() {
- test.equal(q.length(), 0);
- test.done();
- }, 600)
+ test.equal(q.length(), 0);
+ test.done();
+ }, 600);
+},
+
+ 'events': function(test) {
+ test.expect(4);
+ var calls = [];
+ var q = async.queue(function(task, cb) {
+ // nop
+ calls.push('process ' + task);
+ async.setImmediate(cb);
+ }, 10);
+ q.concurrency = 3;
+
+ q.saturated = function() {
+ test.ok(q.length() == 3, 'queue should be saturated now');
+ calls.push('saturated');
+ };
+ q.empty = function() {
+ test.ok(q.length() === 0, 'queue should be empty now');
+ calls.push('empty');
+ };
+ q.drain = function() {
+ test.ok(
+ q.length() === 0 && q.running() === 0,
+ 'queue should be empty now and no more workers should be running'
+ );
+ calls.push('drain');
+ test.same(calls, [
+ 'saturated',
+ 'process foo',
+ 'process bar',
+ 'process zoo',
+ 'foo cb',
+ 'process poo',
+ 'bar cb',
+ 'empty',
+ 'process moo',
+ 'zoo cb',
+ 'poo cb',
+ 'moo cb',
+ 'drain'
+ ]);
+ test.done();
+ };
+ q.push('foo', function () {calls.push('foo cb');});
+ q.push('bar', function () {calls.push('bar cb');});
+ q.push('zoo', function () {calls.push('zoo cb');});
+ q.push('poo', function () {calls.push('poo cb');});
+ q.push('moo', function () {calls.push('moo cb');});
+},
+
+ 'empty': function(test) {
+ test.expect(2);
+ var calls = [];
+ var q = async.queue(function(task, cb) {
+ // nop
+ calls.push('process ' + task);
+ async.setImmediate(cb);
+ }, 3);
+
+ q.drain = function() {
+ test.ok(
+ q.length() === 0 && q.running() === 0,
+ 'queue should be empty now and no more workers should be running'
+ );
+ calls.push('drain');
+ test.same(calls, [
+ 'drain'
+ ]);
+ test.done();
+ };
+ q.push([]);
+},
+
+ 'saturated': function (test) {
+ test.expect(1);
+ 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);
+},
+
+ 'started': function(test) {
+ test.expect(2);
+
+ var q = async.queue(function(task, cb) {
+ cb(null, task);
+ });
+
+ test.equal(q.started, false);
+ q.push([]);
+ test.equal(q.started, true);
+ test.done();
+}
+
};
-exports['priorityQueue'] = function (test) {
+
+exports['priorityQueue'] = {
+
+ 'priorityQueue': function (test) {
+ test.expect(17);
var call_order = [];
// order of completion: 2,1,4,3
var q = async.priorityQueue(function (task, callback) {
- call_order.push('process ' + task);
- callback('error', 'arg');
+ call_order.push('process ' + task);
+ callback('error', 'arg');
}, 1);
q.push(1, 1.4, function (err, arg) {
@@ -2777,9 +3975,10 @@ exports['priorityQueue'] = function (test) {
test.equal(q.length(), 0);
test.done();
};
-};
+},
-exports['priorityQueue concurrency'] = function (test) {
+ 'concurrency': function (test) {
+ test.expect(17);
var call_order = [],
delays = [160,80,240,80];
@@ -2832,9 +4031,15 @@ exports['priorityQueue concurrency'] = function (test) {
test.equal(q.length(), 0);
test.done();
};
+}
+
};
-exports['cargo'] = function (test) {
+
+exports['cargo'] = {
+
+ 'cargo': function (test) {
+ test.expect(19);
var call_order = [],
delays = [160, 160, 80];
@@ -2898,9 +4103,10 @@ exports['cargo'] = function (test) {
test.equal(c.length(), 0);
test.done();
}, 800);
-};
+},
-exports['cargo without callback'] = function (test) {
+ 'without callback': function (test) {
+ test.expect(1);
var call_order = [],
delays = [160,80,240,80];
@@ -2934,9 +4140,10 @@ exports['cargo without callback'] = function (test) {
]);
test.done();
}, 800);
-};
+},
-exports['cargo bulk task'] = function (test) {
+ 'bulk task': function (test) {
+ test.expect(7);
var call_order = [],
delays = [120,40];
@@ -2966,57 +4173,141 @@ exports['cargo bulk task'] = function (test) {
test.equal(c.length(), 0);
test.done();
}, 800);
-};
+},
-exports['cargo drain once'] = function (test) {
-
- var c = async.cargo(function (tasks, callback) {
- callback();
+ 'drain once': function (test) {
+ test.expect(1);
+
+ var c = async.cargo(function (tasks, callback) {
+ callback();
}, 3);
-
+
var drainCounter = 0;
c.drain = function () {
- drainCounter++;
- }
-
+ drainCounter++;
+ };
+
for(var i = 0; i < 10; i++){
- c.push(i);
+ c.push(i);
}
-
+
setTimeout(function(){
- test.equal(drainCounter, 1);
- test.done();
+ test.equal(drainCounter, 1);
+ test.done();
}, 500);
-};
+},
+
+ 'drain twice': function (test) {
+ test.expect(1);
-exports['cargo drain twice'] = function (test) {
-
var c = async.cargo(function (tasks, callback) {
- callback();
+ callback();
}, 3);
-
+
var loadCargo = function(){
- for(var i = 0; i < 10; i++){
- c.push(i);
- }
+ for(var i = 0; i < 10; i++){
+ c.push(i);
+ }
};
-
+
var drainCounter = 0;
c.drain = function () {
- drainCounter++;
- }
+ drainCounter++;
+ };
loadCargo();
setTimeout(loadCargo, 500);
setTimeout(function(){
- test.equal(drainCounter, 2);
- test.done();
+ test.equal(drainCounter, 2);
+ test.done();
}, 1000);
-};
+},
-exports['memoize'] = function (test) {
+ 'events': function(test) {
test.expect(4);
+ var calls = [];
+ var q = async.cargo(function(task, cb) {
+ // nop
+ calls.push('process ' + task);
+ async.setImmediate(cb);
+ }, 1);
+ q.concurrency = 3;
+
+ q.saturated = function() {
+ test.ok(q.length() == 3, 'cargo should be saturated now');
+ calls.push('saturated');
+ };
+ q.empty = function() {
+ test.ok(q.length() === 0, 'cargo should be empty now');
+ calls.push('empty');
+ };
+ q.drain = function() {
+ test.ok(
+ q.length() === 0 && q.running() === 0,
+ 'cargo should be empty now and no more workers should be running'
+ );
+ calls.push('drain');
+ test.same(calls, [
+ 'saturated',
+ 'process foo',
+ 'process bar',
+ 'process zoo',
+ 'foo cb',
+ 'process poo',
+ 'bar cb',
+ 'empty',
+ 'process moo',
+ 'zoo cb',
+ 'poo cb',
+ 'moo cb',
+ 'drain'
+ ]);
+ test.done();
+ };
+ q.push('foo', function () {calls.push('foo cb');});
+ q.push('bar', function () {calls.push('bar cb');});
+ q.push('zoo', function () {calls.push('zoo cb');});
+ q.push('poo', function () {calls.push('poo cb');});
+ q.push('moo', function () {calls.push('moo cb');});
+},
+
+ 'expose payload': function (test) {
+ test.expect(5);
+ var called_once = false;
+ var cargo= async.cargo(function(tasks, cb) {
+ if (!called_once) {
+ test.equal(cargo.payload, 1);
+ test.ok(tasks.length === 1, 'should start with payload = 1');
+ } else {
+ test.equal(cargo.payload, 2);
+ test.ok(tasks.length === 2, 'next call shold have payload = 2');
+ }
+ called_once = true;
+ setTimeout(cb, 25);
+ }, 1);
+
+ cargo.drain = function () {
+ test.done();
+ };
+
+ test.equals(cargo.payload, 1);
+
+ cargo.push([1, 2, 3]);
+
+ setTimeout(function () {
+ cargo.payload = 2;
+ }, 15);
+}
+
+};
+
+
+
+exports['memoize'] = {
+
+ 'memoize': function (test) {
+ test.expect(5);
var call_order = [];
var fn = function (arg1, arg2, callback) {
@@ -3028,6 +4319,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);
@@ -3038,9 +4330,9 @@ exports['memoize'] = function (test) {
});
});
});
-};
+},
-exports['memoize maintains asynchrony'] = function (test) {
+ 'maintains asynchrony': function (test) {
test.expect(3);
var call_order = [];
@@ -3077,9 +4369,9 @@ exports['memoize maintains asynchrony'] = function (test) {
test.same(call_order, async_call_order);
test.done();
}
-};
+},
-exports['unmemoize'] = function(test) {
+ 'unmemoize': function(test) {
test.expect(4);
var call_order = [];
@@ -3103,9 +4395,9 @@ exports['unmemoize'] = function(test) {
});
});
});
-}
+},
-exports['unmemoize a not memoized function'] = function(test) {
+ 'unmemoize a not memoized function': function(test) {
test.expect(1);
var fn = function (arg1, arg2, callback) {
@@ -3118,21 +4410,21 @@ exports['unmemoize a not memoized function'] = function(test) {
});
test.done();
-}
+},
-exports['memoize error'] = function (test) {
+ 'error': function (test) {
test.expect(1);
var testerr = new Error('test');
var fn = function (arg1, arg2, callback) {
callback(testerr, arg1 + arg2);
};
- async.memoize(fn)(1, 2, function (err, result) {
+ async.memoize(fn)(1, 2, function (err) {
test.equal(err, testerr);
});
test.done();
-};
+},
-exports['memoize multiple calls'] = function (test) {
+ 'multiple calls': function (test) {
test.expect(3);
var fn = function (arg1, arg2, callback) {
test.ok(true);
@@ -3148,9 +4440,9 @@ exports['memoize multiple calls'] = function (test) {
test.equal(result, 1, 2);
test.done();
});
-};
+},
-exports['memoize custom hash function'] = function (test) {
+ 'custom hash function': function (test) {
test.expect(2);
var testerr = new Error('test');
@@ -3167,11 +4459,11 @@ exports['memoize custom hash function'] = function (test) {
test.done();
});
});
-};
+},
-exports['memoize manually added memo value'] = function (test) {
+ 'manually added memo value': function (test) {
test.expect(1);
- var fn = async.memoize(function(arg, callback) {
+ var fn = async.memoize(function() {
test(false, "Function should never be called");
});
fn.memo["foo"] = ["bar"];
@@ -3179,156 +4471,180 @@ exports['memoize manually added memo value'] = function (test) {
test.equal(val, "bar");
test.done();
});
-};
+}
-// Issue 10 on github: https://github.com/caolan/async/issues#issue/10
-exports['falsy return values in series'] = function (test) {
- function taskFalse(callback) {
- 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) {
- test.equal(results.length, 4);
- test.strictEqual(results[0], false);
- test.strictEqual(results[1], undefined);
- test.strictEqual(results[2], undefined);
- test.strictEqual(results[3], null);
- test.done();
- }
- );
};
-// Issue 10 on github: https://github.com/caolan/async/issues#issue/10
-exports['falsy return values in parallel'] = function (test) {
- function taskFalse(callback) {
- async.nextTick(function() {
- callback(null, false);
- });
- };
- function taskUndefined(callback) {
- async.nextTick(function() {
- callback(null, undefined);
- });
- };
- function taskEmpty(callback) {
- async.nextTick(function() {
- callback(null);
+
+exports['ensureAsync'] = {
+ 'defer sync functions': function (test) {
+ test.expect(6);
+ var sync = true;
+ async.ensureAsync(function (arg1, arg2, cb) {
+ test.equal(arg1, 1);
+ test.equal(arg2, 2);
+ cb(null, 4, 5);
+ })(1, 2, function (err, arg4, arg5) {
+ test.equal(err, null);
+ test.equal(arg4, 4);
+ test.equal(arg5, 5);
+ test.ok(!sync, 'callback called on same tick');
+ test.done();
});
- };
- function taskNull(callback) {
- async.nextTick(function() {
- callback(null, null);
+ sync = false;
+ },
+
+ 'do not defer async functions': function (test) {
+ test.expect(6);
+ var sync = false;
+ async.ensureAsync(function (arg1, arg2, cb) {
+ test.equal(arg1, 1);
+ test.equal(arg2, 2);
+ async.setImmediate(function () {
+ sync = true;
+ cb(null, 4, 5);
+ sync = false;
+ });
+ })(1, 2, function (err, arg4, arg5) {
+ test.equal(err, null);
+ test.equal(arg4, 4);
+ test.equal(arg5, 5);
+ test.ok(sync, 'callback called on next tick');
+ test.done();
});
- };
- async.parallel(
- [taskFalse, taskUndefined, taskEmpty, taskNull],
- function(err, results) {
- test.equal(results.length, 4);
- test.strictEqual(results[0], false);
- test.strictEqual(results[1], undefined);
- test.strictEqual(results[2], undefined);
- test.strictEqual(results[3], null);
+ },
+
+ 'double wrapping': function (test) {
+ test.expect(6);
+ var sync = true;
+ async.ensureAsync(async.ensureAsync(function (arg1, arg2, cb) {
+ test.equal(arg1, 1);
+ test.equal(arg2, 2);
+ cb(null, 4, 5);
+ }))(1, 2, function (err, arg4, arg5) {
+ test.equal(err, null);
+ test.equal(arg4, 4);
+ test.equal(arg5, 5);
+ test.ok(!sync, 'callback called on same tick');
test.done();
- }
- );
+ });
+ sync = false;
+ }
};
-exports['queue events'] = function(test) {
- var calls = [];
- var q = async.queue(function(task, cb) {
- // nop
- calls.push('process ' + task);
- async.setImmediate(cb);
- }, 10);
- q.concurrency = 3;
-
- q.saturated = function() {
- test.ok(q.length() == 3, 'queue should be saturated now');
- calls.push('saturated');
- };
- q.empty = function() {
- test.ok(q.length() == 0, 'queue should be empty now');
- calls.push('empty');
- };
- q.drain = function() {
- test.ok(
- q.length() == 0 && q.running() == 0,
- 'queue should be empty now and no more workers should be running'
- );
- calls.push('drain');
- test.same(calls, [
- 'saturated',
- 'process foo',
- 'process bar',
- 'process zoo',
- 'foo cb',
- 'process poo',
- 'bar cb',
- 'empty',
- 'process moo',
- 'zoo cb',
- 'poo cb',
- 'moo cb',
- 'drain'
- ]);
+exports['constant'] = function (test) {
+ test.expect(5);
+ var f = async.constant(42, 1, 2, 3);
+ f(function (err, value, a, b, c) {
+ test.ok(!err);
+ test.ok(value === 42);
+ test.ok(a === 1);
+ test.ok(b === 2);
+ test.ok(c === 3);
test.done();
- };
- q.push('foo', function () {calls.push('foo cb');});
- q.push('bar', function () {calls.push('bar cb');});
- q.push('zoo', function () {calls.push('zoo cb');});
- q.push('poo', function () {calls.push('poo cb');});
- q.push('moo', function () {calls.push('moo cb');});
+ });
};
-exports['queue empty'] = function(test) {
- var calls = [];
- var q = async.queue(function(task, cb) {
- // nop
- calls.push('process ' + task);
- async.setImmediate(cb);
- }, 3);
+exports['asyncify'] = {
+ 'asyncify': function (test) {
+ var parse = async.asyncify(JSON.parse);
+ parse("{\"a\":1}", function (err, result) {
+ test.ok(!err);
+ test.ok(result.a === 1);
+ test.done();
+ });
+ },
- q.drain = function() {
- test.ok(
- q.length() == 0 && q.running() == 0,
- 'queue should be empty now and no more workers should be running'
- );
- calls.push('drain');
- test.same(calls, [
- 'drain'
- ]);
+ 'asyncify null': function (test) {
+ var parse = async.asyncify(function() {
+ return null;
+ });
+ parse("{\"a\":1}", function (err, result) {
+ test.ok(!err);
+ test.ok(result === null);
+ test.done();
+ });
+ },
+
+ 'variable numbers of arguments': function (test) {
+ async.asyncify(function (x, y, z) {
+ test.ok(arguments.length === 3);
+ test.ok(x === 1);
+ test.ok(y === 2);
+ test.ok(z === 3);
+ })(1, 2, 3, function () {});
test.done();
- };
- q.push([]);
-};
+ },
-exports['queue started'] = function(test) {
+ 'catch errors': function (test) {
+ async.asyncify(function () {
+ throw new Error("foo");
+ })(function (err) {
+ test.ok(err);
+ test.ok(err.message === "foo");
+ test.done();
+ });
+ },
- var calls = [];
- var q = async.queue(function(task, cb) {});
-
- test.equal(q.started, false);
- q.push([]);
- test.equal(q.started, true);
- test.done();
+ 'dont catch errors in the callback': function (test) {
+ try {
+ async.asyncify(function () {})(function (err) {
+ if (err) {
+ return test.done(new Error("should not get an error here"));
+ }
+ throw new Error("callback error");
+ });
+ } catch (e) {
+ test.ok(e.message === "callback error");
+ test.done();
+ }
+ },
+ 'promisified': [
+ 'native-promise-only',
+ 'bluebird',
+ 'es6-promise',
+ 'rsvp'
+ ].reduce(function(promises, name) {
+ if (isBrowser()) {
+ // node only test
+ return;
+ }
+ var Promise = require(name);
+ if (typeof Promise.Promise === 'function') {
+ Promise = Promise.Promise;
+ }
+ promises[name] = {
+ 'resolve': function(test) {
+ var promisified = function(argument) {
+ return new Promise(function (resolve) {
+ setTimeout(function () {
+ resolve(argument + " resolved");
+ }, 15);
+ });
+ };
+ async.asyncify(promisified)("argument", function (err, value) {
+ if (err) {
+ return test.done(new Error("should not get an error here"));
+ }
+ test.ok(value === "argument resolved");
+ test.done();
+ });
+ },
+
+ 'reject': function(test) {
+ var promisified = function(argument) {
+ return new Promise(function (resolve, reject) {
+ reject(argument + " rejected");
+ });
+ };
+ async.asyncify(promisified)("argument", function (err) {
+ test.ok(err);
+ test.ok(err.message === "argument rejected");
+ test.done();
+ });
+ }
+ };
+ return promises;
+ }, {})
};
-