summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--LICENSE2
-rw-r--r--README.md875
-rw-r--r--component.json11
-rwxr-xr-xlib/async.js107
-rw-r--r--package.json8
-rwxr-xr-xtest/test-async.js337
6 files changed, 913 insertions, 427 deletions
diff --git a/LICENSE b/LICENSE
index b7f9d50..8f29698 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2010 Caolan McMahon
+Copyright (c) 2010-2014 Caolan McMahon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index 470757e..f7d4f7a 100644
--- a/README.md
+++ b/README.md
@@ -5,14 +5,14 @@
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.
+use with [Node.js](http://nodejs.org), it can also be used directly in the
+browser. Also supports [component](https://github.com/component/component).
Async provides around 20 functions that include the usual 'functional'
-suspects (map, reduce, filter, each…) as well as some common patterns
-for asynchronous control flow (parallel, series, waterfall…). All these
-functions assume you follow the node.js convention of providing a single
-callback as the last argument of your async function.
+suspects (`map`, `reduce`, `filter`, `each`…) as well as some common patterns
+for asynchronous control flow (`parallel`, `series`, `waterfall`…). All these
+functions assume you follow the Node.js convention of providing a single
+callback as the last argument of your `async` function.
## Quick Examples
@@ -22,7 +22,7 @@ async.map(['file1','file2','file3'], fs.stat, function(err, results){
// results is now an array of stats for each file
});
-async.filter(['file1','file2','file3'], path.exists, function(results){
+async.filter(['file1','file2','file3'], fs.exists, function(results){
// results now equals an array of the existing files
});
@@ -45,8 +45,8 @@ missing please create a GitHub issue for it.
### Binding a context to an iterator
-This section is really about bind, not about async. If you are wondering how to
-make async execute your iterators in a given context, or are confused as to why
+This section is really about `bind`, not about `async`. If you are wondering how to
+make `async` execute your iterators in a given context, or are confused as to why
a method of another library isn't working as an iterator, study this example:
```js
@@ -81,7 +81,7 @@ async.map([1, 2, 3], AsyncSquaringLibrary.square.bind(AsyncSquaringLibrary), fun
The source is available for download from
[GitHub](http://github.com/caolan/async).
-Alternatively, you can install using Node Package Manager (npm):
+Alternatively, you can install using Node Package Manager (`npm`):
npm install async
@@ -89,7 +89,9 @@ __Development:__ [async.js](https://github.com/caolan/async/raw/master/lib/async
## In the Browser
-So far it's been tested in IE6, IE7, IE8, FF3.6 and Chrome 5. Usage:
+So far it's been tested in IE6, IE7, IE8, FF3.6 and Chrome 5.
+
+Usage:
```html
<script type="text/javascript" src="async.js"></script>
@@ -106,45 +108,57 @@ So far it's been tested in IE6, IE7, IE8, FF3.6 and Chrome 5. Usage:
### Collections
-* [each](#each)
-* [map](#map)
-* [filter](#filter)
-* [reject](#reject)
-* [reduce](#reduce)
-* [detect](#detect)
-* [sortBy](#sortBy)
-* [some](#some)
-* [every](#every)
-* [concat](#concat)
+* [`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)
+* [`sortBy`](#sortBy)
+* [`some`](#some)
+* [`every`](#every)
+* [`concat`](#concat)
+* [`concatSeries`](#concatSeries)
### Control Flow
-* [series](#series)
-* [parallel](#parallel)
-* [whilst](#whilst)
-* [doWhilst](#doWhilst)
-* [until](#until)
-* [doUntil](#doUntil)
-* [forever](#forever)
-* [waterfall](#waterfall)
-* [compose](#compose)
-* [applyEach](#applyEach)
-* [queue](#queue)
-* [cargo](#cargo)
-* [auto](#auto)
-* [iterator](#iterator)
-* [apply](#apply)
-* [nextTick](#nextTick)
-* [times](#times)
-* [timesSeries](#timesSeries)
+* [`series`](#series)
+* [`parallel`](#parallel)
+* [`parallelLimit`](#parallellimittasks-limit-callback)
+* [`whilst`](#whilst)
+* [`doWhilst`](#doWhilst)
+* [`until`](#until)
+* [`doUntil`](#doUntil)
+* [`forever`](#forever)
+* [`waterfall`](#waterfall)
+* [`compose`](#compose)
+* [`seq`](#seq)
+* [`applyEach`](#applyEach)
+* [`applyEachSeries`](#applyEachSeries)
+* [`queue`](#queue)
+* [`cargo`](#cargo)
+* [`auto`](#auto)
+* [`iterator`](#iterator)
+* [`apply`](#apply)
+* [`nextTick`](#nextTick)
+* [`times`](#times)
+* [`timesSeries`](#timesSeries)
### Utils
-* [memoize](#memoize)
-* [unmemoize](#unmemoize)
-* [log](#log)
-* [dir](#dir)
-* [noConflict](#noConflict)
+* [`memoize`](#memoize)
+* [`unmemoize`](#unmemoize)
+* [`log`](#log)
+* [`dir`](#dir)
+* [`noConflict`](#noConflict)
## Collections
@@ -153,25 +167,26 @@ So far it's been tested in IE6, IE7, IE8, FF3.6 and Chrome 5. Usage:
<a name="each" />
### each(arr, iterator, callback)
-Applies an iterator function to each item in an array, in parallel.
-The iterator is called with an item from the list and a callback for when it
-has finished. If the iterator passes an error to this callback, the main
-callback for the each function is immediately called with the error.
+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
+has finished. If the `iterator` passes an error to its `callback`, the main
+`callback` (for the `each` function) is immediately called with the error.
-Note, that since this function applies the iterator to each item in parallel
+Note, that since this function applies `iterator` to each item in parallel,
there is no guarantee that the iterator functions will complete in order.
__Arguments__
-* arr - An array to iterate over.
-* iterator(item, callback) - A function to apply to each item in the array.
- The iterator is passed a callback(err) which must be called once it has
- completed. If no error has occured, the callback should be run without
- arguments or with an explicit null argument.
-* callback(err) - A callback which is called after all the iterator functions
- have finished, or an error has occurred.
+* `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 occured, 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.
+
+__Examples__
-__Example__
```js
// assuming openFiles is an array of file names and saveFile is a function
@@ -182,15 +197,46 @@ async.each(openFiles, saveFile, function(err){
});
```
+```js
+// assuming openFiles is an array of file names and saveFile is a function
+// to save the modified contents of that file:
+
+async.each(openFiles, function( file, callback) {
+
+ // Perform operation on file here.
+ console.log('Processing file ' + file);
+ callback();
+
+ if( file.length > 32 ) {
+ console.log('This file name is too long');
+ callback('File name too long');
+
+ return;
+ } else {
+ console.log('File saved');
+ callback();
+ }
+}, function(err){
+ // if any of the saves produced an error, err would equal that error
+ if( err ) {
+ // One of the iterations produced an error.
+ // All processing will now stop.
+ console.log('A file failed to process');
+ } else {
+ console.log('All files have been processed successfully');
+ }
+});
+```
+
---------------------------------------
<a name="forEachSeries" />
<a name="eachSeries" />
### eachSeries(arr, iterator, callback)
-The same as each only the iterator is applied to each item in the array in
-series. The next iterator is only called once the current one has completed
-processing. This means the iterator functions will complete in order.
+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.
---------------------------------------
@@ -199,23 +245,22 @@ processing. This means the iterator functions will complete in order.
<a name="eachLimit" />
### eachLimit(arr, limit, iterator, callback)
-The same as each only no more than "limit" iterators will be simultaneously
+The same as [`each`](#each), 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.
+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.
__Arguments__
-* arr - An array to iterate over.
-* limit - The maximum number of iterators to run at any time.
-* iterator(item, callback) - A function to apply to each item in the array.
- The iterator is passed a callback(err) which must be called once it has
+* `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 occured, the callback should be run without
- arguments or with an explicit null argument.
-* callback(err) - A callback which is called after all the iterator functions
- have finished, or an error has occurred.
+ arguments or with an explicit `null` argument.
+* `callback(err)` - A callback which is called when all `iterator` functions
+ have finished, or an error occurs.
__Example__
@@ -233,26 +278,25 @@ async.eachLimit(documents, 20, requestApi, function(err){
<a name="map" />
### map(arr, iterator, callback)
-Produces a new array of values by mapping each value in the given array through
-the iterator function. The iterator is called with an item from the array and a
-callback for when it has finished processing. The callback takes 2 arguments,
-an error and the transformed item from the array. If the iterator passes an
-error to this callback, the main callback for the map function is immediately
-called with the error.
+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 this
+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, however
-the results array will be in the same order as the original array.
+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.
+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 the array.
- 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 after all the iterator
- functions have finished, or an error has occurred. Results is an array of the
- transformed items from the original array.
+* `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
+ 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`
+ functions have finished, or an error occurs. Results is an array of the
+ transformed items from the `arr`.
__Example__
@@ -267,9 +311,9 @@ async.map(['file1','file2','file3'], fs.stat, function(err, results){
<a name="mapSeries" />
### mapSeries(arr, iterator, callback)
-The same as map only the iterator is applied to each item in the array in
-series. The next iterator is only called once the current one has completed
-processing. The results array will be in the same order as the original.
+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.
---------------------------------------
@@ -277,87 +321,88 @@ processing. The results array will be in the same order as the original.
<a name="mapLimit" />
### mapLimit(arr, limit, iterator, callback)
-The same as map only no more than "limit" iterators will be simultaneously
+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.
+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 iterators to run at any time.
-* iterator(item, callback) - A function to apply to each item in the array.
- 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 after all the iterator
- functions have finished, or an error has occurred. Results is an array of the
- transformed items from the original array.
+* `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.map(['file1','file2','file3'], 1, fs.stat, function(err, results){
+async.mapLimit(['file1','file2','file3'], 1, fs.stat, function(err, results){
// results is now an array of stats for each file
});
```
---------------------------------------
+<a name="select" />
<a name="filter" />
### filter(arr, iterator, callback)
-__Alias:__ select
+__Alias:__ `select`
-Returns a new array of all the values which pass an async truth test.
-_The callback for each iterator call only accepts a single argument of true or
-false, it does not accept an error argument first!_ This is in-line with the
-way node libraries work with truth tests like path.exists. This operation is
+Returns a new array of all the values in `arr` which pass an async truth test.
+_The callback for each `iterator` call only accepts a single argument of `true` or
+`false`; it does not accept an error argument first!_ This is in-line with the
+way node libraries work with truth tests like `fs.exists`. This operation is
performed in parallel, but the results array will be in the same order as the
original.
__Arguments__
-* arr - An array to iterate over.
-* iterator(item, callback) - A truth test to apply to each item in the array.
- The iterator is passed a callback(truthValue) which must be called with a
+* `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(results) - A callback which is called after all the iterator
+* `callback(results)` - A callback which is called after all the `iterator`
functions have finished.
__Example__
```js
-async.filter(['file1','file2','file3'], path.exists, function(results){
+async.filter(['file1','file2','file3'], fs.exists, function(results){
// results now equals an array of the existing files
});
```
---------------------------------------
+<a name="selectSeries" />
<a name="filterSeries" />
### filterSeries(arr, iterator, callback)
-__alias:__ selectSeries
+__Alias:__ `selectSeries`
-The same as filter only the iterator is applied to each item in the array in
-series. The next iterator is only called once the current one has completed
-processing. The results array will be in the same order as the original.
+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.
---------------------------------------
<a name="reject" />
### reject(arr, iterator, callback)
-The opposite of filter. Removes values that pass an async truth test.
+The opposite of [`filter`](#filter). Removes values that pass an `async` truth test.
---------------------------------------
<a name="rejectSeries" />
### rejectSeries(arr, iterator, callback)
-The same as reject, only the iterator is applied to each item in the array
+The same as [`reject`](#reject), only the `iterator` is applied to each item in `arr`
in series.
@@ -366,27 +411,28 @@ in series.
<a name="reduce" />
### reduce(arr, memo, iterator, callback)
-__aliases:__ inject, foldl
+__Aliases:__ `inject`, `foldl`
-Reduces a list of values 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. For performance reasons, it may make sense to
-split a call to this function into a parallel map, 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.
+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.
+
+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__
-* arr - An array to iterate over.
-* 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
+* `arr` - An array to iterate over.
+* `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
+ 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)` - A callback which is called after all the `iterator`
functions have finished. Result is the reduced value.
__Example__
@@ -407,9 +453,9 @@ async.reduce([1,2,3], 0, function(memo, item, callback){
<a name="reduceRight" />
### reduceRight(arr, memo, iterator, callback)
-__Alias:__ foldr
+__Alias:__ `foldr`
-Same as reduce, only operates on the items in the array in reverse order.
+Same as [`reduce`](#reduce), only operates on `arr` in reverse order.
---------------------------------------
@@ -417,28 +463,28 @@ Same as reduce, only operates on the items in the array in reverse order.
<a name="detect" />
### detect(arr, iterator, callback)
-Returns the first value in a list that passes an async truth test. The
-iterator is applied in parallel, meaning the first iterator to return true will
-fire the detect callback with that result. That means the result might not be
-the first item in the original array (in terms of order) that passes the test.
+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
+fire the detect `callback` with that result. That means the result might not be
+the first item in the original `arr` (in terms of order) that passes the test.
-If order within the original array is important then look at detectSeries.
+If order within the original `arr` is important, then look at [`detectSeries`](#detectSeries).
__Arguments__
-* arr - An array to iterate over.
-* iterator(item, callback) - A truth test to apply to each item in the array.
- The iterator is passed a callback(truthValue) which must be called with a
+* `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
- true, or after all the iterator functions have finished. Result will be
+* `callback(result)` - A callback which is called as soon as any iterator returns
+ `true`, or after all the `iterator` functions have finished. Result will be
the first item in the array that passes the truth test (iterator) or the
- value undefined if none passed.
+ value `undefined` if none passed.
__Example__
```js
-async.detect(['file1','file2','file3'], path.exists, function(result){
+async.detect(['file1','file2','file3'], fs.exists, function(result){
// result now equals the first file in the list that exists
});
```
@@ -448,8 +494,8 @@ async.detect(['file1','file2','file3'], path.exists, function(result){
<a name="detectSeries" />
### detectSeries(arr, iterator, callback)
-The same as detect, only the iterator is applied to each item in the array
-in series. This means the result is always the first in the original array (in
+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.
@@ -458,18 +504,18 @@ terms of array order) that passes the truth test.
<a name="sortBy" />
### sortBy(arr, iterator, callback)
-Sorts a list by the results of running each value through an async iterator.
+Sorts a list by the results of running each `arr` value through an async `iterator`.
__Arguments__
-* arr - An array to iterate over.
-* iterator(item, callback) - A function to apply to each item in the array.
- 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
+* `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, 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
- functions have finished, or an error has occurred. Results is the items from
- the original array sorted by the values returned by the iterator calls.
+* `callback(err, results)` - 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.
__Example__
@@ -489,28 +535,28 @@ async.sortBy(['file1','file2','file3'], function(file, callback){
<a name="some" />
### some(arr, iterator, callback)
-__Alias:__ any
+__Alias:__ `any`
-Returns true if at least one element in the array satisfies an async test.
-_The callback for each iterator call only accepts a single argument of true or
-false, it does not accept an error argument first!_ This is in-line with the
-way node libraries work with truth tests like path.exists. Once any iterator
-call returns true, the main callback is immediately called.
+Returns `true` if at least one element in the `arr` satisfies an async test.
+_The callback for each iterator call only accepts a single argument of `true` or
+`false`; it does not accept an error argument first!_ This is in-line with the
+way node libraries work with truth tests like `fs.exists`. Once any iterator
+call returns `true`, the main `callback` is immediately called.
__Arguments__
-* arr - An array to iterate over.
-* iterator(item, callback) - A truth test to apply to each item in the array.
- The iterator is passed a callback(truthValue) which must be called with a
- boolean argument once it has completed.
-* callback(result) - A callback which is called as soon as any iterator returns
- true, or after all the iterator functions have finished. Result will be
- either true or false depending on the values of the async tests.
+* `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
+ called with a boolean argument once it has completed.
+* `callback(result)` - A callback which is called as soon as any iterator returns
+ `true`, or after all the iterator functions have finished. Result will be
+ either `true` or `false` depending on the values of the async tests.
__Example__
```js
-async.some(['file1','file2','file3'], path.exists, function(result){
+async.some(['file1','file2','file3'], fs.exists, function(result){
// if result is true then at least one of the files exists
});
```
@@ -520,27 +566,27 @@ async.some(['file1','file2','file3'], path.exists, function(result){
<a name="every" />
### every(arr, iterator, callback)
-__Alias:__ all
+__Alias:__ `all`
-Returns true if every element in the array satisfies an async test.
-_The callback for each iterator call only accepts a single argument of true or
-false, it does not accept an error argument first!_ This is in-line with the
-way node libraries work with truth tests like path.exists.
+Returns `true` if every element in `arr` satisfies an async test.
+_The callback for each `iterator` call only accepts a single argument of `true` or
+`false`; it does not accept an error argument first!_ This is in-line with the
+way node libraries work with truth tests like `fs.exists`.
__Arguments__
-* arr - An array to iterate over.
-* iterator(item, callback) - A truth test to apply to each item in the array.
- 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
+* `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
+ 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.
__Example__
```js
-async.every(['file1','file2','file3'], path.exists, function(result){
+async.every(['file1','file2','file3'], fs.exists, function(result){
// if result is true then every file exists
});
```
@@ -550,20 +596,20 @@ async.every(['file1','file2','file3'], path.exists, function(result){
<a name="concat" />
### concat(arr, iterator, callback)
-Applies an iterator to each item in a list, concatenating the results. Returns the
-concatenated list. The iterators are called in parallel, and the results are
+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
concatenated as they return. There is no guarantee that the results array will
-be returned in the original order of the arguments passed to the iterator function.
+be returned in the original order of `arr` passed to the `iterator` function.
__Arguments__
-* arr - An array to iterate over
-* iterator(item, callback) - A function to apply to each item in the array.
- 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
- functions have finished, or an error has occurred. Results is an array containing
- the concatenated results of the iterator function.
+* `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
+ 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`
+ functions have finished, or an error occurs. Results is an array containing
+ the concatenated results of the `iterator` function.
__Example__
@@ -578,7 +624,7 @@ async.concat(['dir1','dir2','dir3'], fs.readdir, function(err, files){
<a name="concatSeries" />
### concatSeries(arr, iterator, callback)
-Same as async.concat, but executes in series instead of parallel.
+Same as [`concat`](#concat), but executes in series instead of parallel.
## Control Flow
@@ -586,26 +632,33 @@ Same as async.concat, but executes in series instead of parallel.
<a name="series" />
### series(tasks, [callback])
-Run an array of functions in series, each one running once the previous
+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 the callback for the series 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.
+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
-run as a function and the results will be passed to the final callback as an object
+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
-async.series.
+[`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)
+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.
__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 (which can
- be null) and an optional result value.
-* callback(err, results) - An optional callback to run once all the functions
+* `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.
+* `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.
+ the result arguments passed to the `task` callbacks.
__Example__
@@ -649,24 +702,24 @@ function(err, results) {
<a name="parallel" />
### parallel(tasks, [callback])
-Run an array of functions in parallel, without waiting until the previous
+Run the `tasks` array of functions in parallel, without waiting until the previous
function has completed. If any of the functions pass an error to its
-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
+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.
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
+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
-async.parallel.
+[`parallel`](#parallel).
__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 (which can
- be null) and an optional result value.
-* callback(err, results) - An optional callback to run once all the functions
+* `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
the result arguments passed to the task callbacks.
@@ -712,41 +765,41 @@ function(err, results) {
---------------------------------------
-<a name="parallel" />
+<a name="parallelLimit" />
### parallelLimit(tasks, limit, [callback])
-The same as parallel only the tasks are executed in parallel with a maximum of "limit"
-tasks executing at any time.
+The same as [`parallel`](#parallel), only `tasks` are executed in parallel
+with a maximum of `limit` tasks executing at any time.
-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.
+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 (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
+* `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.
+ the result arguments passed to the `task` callbacks.
---------------------------------------
<a name="whilst" />
### whilst(test, fn, callback)
-Repeatedly call fn, while test returns true. Calls the callback when stopped,
+Repeatedly call `fn`, while `test` returns `true`. Calls `callback` when stopped,
or an error occurs.
__Arguments__
-* test() - synchronous truth test to perform before each execution of fn.
-* fn(callback) - A function to call each time the test passes. The function is
- passed a callback(err) which must be called once it has completed with an
- optional error argument.
-* callback(err) - A callback which is called after the test fails and repeated
- execution of fn has stopped.
+* `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
+ optional `err` argument.
+* `callback(err)` - A callback which is called after the test fails and repeated
+ execution of `fn` has stopped.
__Example__
@@ -770,51 +823,69 @@ async.whilst(
<a name="doWhilst" />
### doWhilst(fn, test, callback)
-The post check version of whilst. To reflect the difference in the order of operations `test` and `fn` arguments are switched. `doWhilst` is to `whilst` as `do while` is to `while` in plain JavaScript.
+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.
---------------------------------------
<a name="until" />
### until(test, fn, callback)
-Repeatedly call fn, until test returns true. Calls the callback when stopped,
+Repeatedly call `fn` until `test` returns `true`. Calls `callback` when stopped,
or an error occurs.
-The inverse of async.whilst.
+The inverse of [`whilst`](#whilst).
---------------------------------------
<a name="doUntil" />
### doUntil(fn, test, callback)
-Like doWhilst except the test is inverted. Note the argument ordering differs from `until`.
+Like [`doWhilst`](#doWhilst), except the `test` is inverted. Note the argument ordering differs from `until`.
---------------------------------------
<a name="forever" />
-### forever(fn, callback)
+### forever(fn, errback)
+
+Calls the asynchronous function `fn` with a callback parameter that allows it to
+call itself again, in series, indefinitely.
-Calls the asynchronous function 'fn' repeatedly, in series, indefinitely.
-If an error is passed to fn's callback then 'callback' is called with the
-error, otherwise it will never be called.
+If an error is passed to the callback then `errback` is called with the
+error, and execution stops, otherwise it will never be called.
+
+```js
+async.forever(
+ function(next) {
+ // next is suitable for passing to things that need a callback(err [, whatever]);
+ // it will result in this function being called again.
+ },
+ function(err) {
+ // if next is called with a value in its first parameter, it will appear
+ // in here as 'err', and execution will stop.
+ }
+);
+```
---------------------------------------
<a name="waterfall" />
### waterfall(tasks, [callback])
-Runs an array of functions in series, each passing their results to the next in
-the array. However, if any of the functions pass an error to the callback, the
-next function is not executed and the main callback is immediately called with
+Runs the `tasks` array of functions in series, each passing their results to the next in
+the array. However, if any of the `tasks` pass an error to their own callback, the
+next function is not executed, and the main `callback` is immediately called with
the error.
__Arguments__
-* 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
+* `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
passed as arguments in order to the next task.
-* callback(err, [results]) - An optional callback to run once all the functions
+* `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.
@@ -827,6 +898,7 @@ async.waterfall([
callback(null, 'one', 'two');
},
function(arg1, arg2, callback){
+ // arg1 now equals 'one' and arg2 now equals 'two'
callback(null, 'three');
},
function(arg1, callback){
@@ -844,14 +916,14 @@ async.waterfall([
Creates a function which is a composition of the passed asynchronous
functions. Each function consumes the return value of the function that
-follows. Composing functions f(), g() and h() would produce the result of
-f(g(h())), only this version uses callbacks to obtain the return values.
+follows. Composing functions `f()`, `g()`, and `h()` would produce the result of
+`f(g(h()))`, only this version uses callbacks to obtain the return values.
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__
@@ -877,19 +949,66 @@ add1mul3(4, function (err, result) {
```
---------------------------------------
+<a name="seq" />
+### 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 is executed with the `this` binding of the composed function.
+
+__Arguments__
+
+* functions... - the asynchronous functions to compose
+
+
+__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
+// 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;
+ asyc.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) {
+ response.json({ status: 'ok', message: 'Cats found', data: cats });
+ }
+ )(req.session.user_id);
+ }
+});
+```
+
+---------------------------------------
<a name="applyEach" />
### applyEach(fns, args..., callback)
-Applies the provided arguments to each function in the array, calling the
-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
+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.
__Arguments__
-* fns - the asynchronous functions to all call with the same arguments
-* args... - any number of separate arguments to pass to the function
-* callback - the final argument should be the callback, called when all
+* `fns` - the asynchronous functions to all call with the same arguments
+* `args...` - any number of separate arguments to pass to the function
+* `callback` - the final argument should be the callback, called when all
functions have completed processing
@@ -911,42 +1030,43 @@ async.each(
<a name="applyEachSeries" />
### applyEachSeries(arr, iterator, callback)
-The same as applyEach only the functions are applied in series.
+The same as [`applyEach`](#applyEach) only the functions are applied in series.
---------------------------------------
<a name="queue" />
### queue(worker, concurrency)
-Creates a queue object with the specified concurrency. Tasks added to the
-queue will be processed in parallel (up to the concurrency limit). If all
-workers are in progress, the task is queued until one is available. Once
-a worker has completed a task, the task's callback is called.
+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.
+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.
-* concurrency - An integer for determining how many worker functions should be
+* `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.
+* `concurrency` - An `integer` for determining how many `worker` functions should be
run in parallel.
__Queue objects__
-The queue object returned by this function has the following properties and
+The `queue` object returned by this function has the following properties and
methods:
-* length() - a function returning the number of items waiting to be processed.
-* 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
+* `length()` - a function returning the number of items waiting to be processed.
+* `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, the callback is called
- once the worker has finished processing the task.
- instead of a single task, an array of tasks can be submitted. the respective callback is used for every task in the list.
-* 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 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
+* `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,
+ and further tasks will be queued.
+* `empty` - a callback that is called when the last item from the `queue` is given to a `worker`.
+* `drain` - a callback that is called when the last item from the `queue` has returned from the `worker`.
__Example__
@@ -991,34 +1111,38 @@ q.unshift({name: 'bar'}, function (err) {
<a name="cargo" />
### cargo(worker, [payload])
-Creates a cargo object with the specified payload. Tasks added to the
-cargo will be processed altogether (up to the payload limit). If the
-worker is in progress, the task is queued until it is available. Once
-the worker has completed some tasks, each callback of those tasks is called.
+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.
+
+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
+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
- an optional error as an argument.
-* payload - An optional integer for determining how many tasks should be
+* `worker(tasks, callback)` - An asynchronous function for processing an array of
+ 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.
__Cargo objects__
-The cargo object returned by this function has the following properties and
+The `cargo` object returned by this function has the following properties and
methods:
-* length() - a function returning the number of items waiting to be processed.
-* payload - an integer for determining how many tasks should be
- process per round. This property can be changed after a cargo is created to
+* `length()` - A function returning the number of items waiting to be processed.
+* `payload` - An `integer` for determining how many tasks should be
+ process per round. This property can be changed after a `cargo` is created to
alter the payload on-the-fly.
-* push(task, [callback]) - add a new task to the queue, the callback is called
- once the worker has finished processing the task.
- instead of a single task, an array of tasks can be submitted. the respective callback is used for every task in the list.
-* saturated - a callback that is called when the queue length hits the concurrency and further tasks will be queued
-* empty - a callback that is called when the last item from the queue is given to a worker
-* drain - a callback that is called when the last item from the queue has returned from the worker
+* `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`
+ can be submitted. The respective callback is used for every task in the list.
+* `saturated` - A callback that is called when the `queue.length()` hits the concurrency and further tasks will be queued.
+* `empty` - A callback that is called when the last item from the `queue` is given to a `worker`.
+* `drain` - A callback that is called when the last item from the `queue` has returned from the `worker`.
__Example__
@@ -1051,33 +1175,36 @@ cargo.push({name: 'baz'}, function (err) {
<a name="auto" />
### auto(tasks, [callback])
-Determines the best order for running functions 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, that function will not complete
-(so any other functions depending on it will not run) and the main callback
-will be called immediately with the error. Functions also receive an object
-containing the results of functions which have completed so far.
+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.
+
+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.
-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. For example, this snippet of code:
+For example, this snippet of code:
```js
async.auto({
- readData: async.apply(fs.readFile, 'data.txt', 'utf-8');
+ readData: async.apply(fs.readFile, 'data.txt', 'utf-8')
}, callback);
```
-will have the effect of calling readFile with the results object as the last
+will have the effect of calling `readFile` with the results object as the last
argument, which will fail:
```js
fs.readFile('data.txt', 'utf-8', cb, {});
```
-Instead, wrap the call to readFile in a function which does not forward the
-results object:
+Instead, wrap the call to `readFile` in a function which does not forward the
+`results` object:
```js
async.auto({
@@ -1089,40 +1216,50 @@ async.auto({
__Arguments__
-* tasks - An object literal containing named functions or an array of
- requirements, with the function itself the last item in the array. The key
+* `tasks` - An object literal (containing named functions) or an array (of
+ requirements, with the function itself the last item in the array). The key
used for each function or array is used when specifying requirements. 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
+ 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.
-* callback(err, results) - An optional callback which is called when all the
- tasks have been completed. The callback will receive an error as an argument
- if any tasks pass an error to their callback. Results will always be passed
- but if an error occurred, no other tasks will be performed, and the results
- object will only contain partial results.
-
+* `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
+ an error occurs, no further `tasks` will be performed, and the results
+ object will only contain partial results.
+
__Example__
```js
async.auto({
get_data: function(callback){
+ console.log('in get_data');
// async code to get some data
+ callback(null, 'data', 'converted to array');
},
make_folder: function(callback){
+ console.log('in make_folder');
// async code to create a directory to store a file in
// this is run at the same time as getting the data
+ callback(null, 'folder');
},
- write_file: ['get_data', 'make_folder', function(callback){
+ write_file: ['get_data', 'make_folder', function(callback, results){
+ console.log('in write_file', JSON.stringify(results));
// once there is some data and the directory exists,
// write the data to a file in the directory
- callback(null, filename);
+ callback(null, 'filename');
}],
email_link: ['write_file', function(callback, results){
+ console.log('in email_link', JSON.stringify(results));
// once the file is written let's email a link to it...
// results.write_file contains the filename returned by write_file.
+ callback(null, {'file':results.write_file, 'email':'user@example.com'});
}]
+}, function(err, results) {
+ console.log('err = ', err);
+ console.log('results = ', results);
});
```
@@ -1132,28 +1269,37 @@ series functions would look like this:
```js
async.parallel([
function(callback){
+ console.log('in get_data');
// async code to get some data
+ callback(null, 'data', 'converted to array');
},
function(callback){
+ console.log('in make_folder');
// async code to create a directory to store a file in
// this is run at the same time as getting the data
+ callback(null, 'folder');
}
],
function(err, results){
async.series([
function(callback){
+ console.log('in write_file', JSON.stringify(results));
// once there is some data and the directory exists,
// write the data to a file in the directory
+ results.push('filename');
+ callback(null);
},
function(callback){
+ console.log('in email_link', JSON.stringify(results));
// once the file is written let's email a link to it...
+ callback(null, {'file':results.pop(), 'email':'user@example.com'});
}
]);
});
```
-For a complicated series of async tasks using the auto function makes adding
-new tasks much easier and makes the code more readable.
+For a complicated series of `async` tasks, using the [`auto`](#auto) function makes adding
+new tasks much easier (and the code more readable).
---------------------------------------
@@ -1161,16 +1307,16 @@ new tasks much easier and makes the code more readable.
<a name="iterator" />
### iterator(tasks)
-Creates an iterator function which calls the next function in the array,
+Creates an iterator function which calls the next function in the `tasks` array,
returning a continuation to call the next one after that. It's also possible to
-'peek' the next iterator by doing iterator.next().
+“peek” at the next iterator with `iterator.next()`.
-This function is used internally by the async module but can be useful when
+This function is used internally by the `async` module, but can be useful when
you want to manually control the flow of functions in series.
__Arguments__
-* tasks - An array of functions to run.
+* `tasks` - An array of functions to run.
__Example__
@@ -1197,15 +1343,16 @@ node> nextfn();
<a name="apply" />
### apply(function, arguments..)
-Creates a continuation function with some arguments already applied, a useful
-shorthand when combined with other control flow functions. Any arguments
+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
to apply.
__Arguments__
-* function - The function you want to eventually apply all arguments to.
-* arguments... - Any number of arguments to automatically apply when the
+* `function` - The function you want to eventually apply all arguments to.
+* `arguments...` - Any number of arguments to automatically apply when the
continuation is called.
__Example__
@@ -1247,16 +1394,16 @@ three
<a name="nextTick" />
### nextTick(callback)
-Calls the 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)
-if available, otherwise setTimeout(callback, 0), which means other higher priority
-events may precede the execution of the 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)`
+if available, otherwise `setTimeout(callback, 0)`, which means other higher priority
+events may precede the execution of `callback`.
This is used internally for browser-compatibility purposes.
__Arguments__
-* callback - The function to call on a later loop around the event loop.
+* `callback` - The function to call on a later loop around the event loop.
__Example__
@@ -1272,13 +1419,13 @@ call_order.push('one')
<a name="times" />
### times(n, callback)
-Calls the callback n times and accumulates results in the same manner
-you would use with async.map.
+Calls the `callback` 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.
+* `n` - The number of times to run the function.
+* `callback` - The function to call `n` times.
__Example__
@@ -1302,9 +1449,9 @@ async.times(5, function(n, next){
<a name="timesSeries" />
### timesSeries(n, callback)
-The same as times only the iterator is applied to each item in the array in
-series. The next iterator is only called once the current one has completed
-processing. The results array will be in the same order as the original.
+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.
## Utils
@@ -1312,7 +1459,7 @@ processing. The results array will be in the same order as the original.
<a name="memoize" />
### memoize(fn, [hasher])
-Caches the results of an async function. When creating a hash to store function
+Caches the results of an `async` function. When creating a hash to store function
results against, the callback is omitted from the hash and an optional hash
function can be used.
@@ -1321,9 +1468,9 @@ by `memoize`.
__Arguments__
-* fn - the function you to proxy and cache results from.
-* 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
+* `fn` - The function to proxy and cache results from.
+* `hasher` - Tn 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.
__Example__
@@ -1344,25 +1491,25 @@ fn('some name', function () {
<a name="unmemoize" />
### unmemoize(fn)
-Undoes a memoized function, reverting it to the original, unmemoized
-form. Comes handy in tests.
+Undoes a [`memoize`](#memoize)d function, reverting it to the original, unmemoized
+form. Handy for testing.
__Arguments__
-* fn - the memoized function
+* `fn` - the memoized function
<a name="log" />
### log(function, arguments)
-Logs the result of an async function to the console. Only works in node.js or
-in browsers that support console.log and console.error (such as FF and Chrome).
-If multiple arguments are returned from the async function, console.log is
+Logs the result of an `async` function to the `console`. Only works in Node.js or
+in browsers that support `console.log` and `console.error` (such as FF and Chrome).
+If multiple arguments are returned from the async function, `console.log` is
called on each argument in order.
__Arguments__
-* function - The function you want to eventually apply all arguments to.
-* arguments... - Any number of arguments to apply to the function.
+* `function` - The function you want to eventually apply all arguments to.
+* `arguments...` - Any number of arguments to apply to the function.
__Example__
@@ -1383,16 +1530,16 @@ node> async.log(hello, 'world');
<a name="dir" />
### dir(function, arguments)
-Logs the result of an async function to the console using console.dir to
-display the properties of the resulting object. Only works in node.js or
-in browsers that support console.dir and console.error (such as FF and Chrome).
-If multiple arguments are returned from the async function, console.dir is
+Logs the result of an `async` function to the `console` using `console.dir` to
+display the properties of the resulting object. Only works in Node.js or
+in browsers that support `console.dir` and `console.error` (such as FF and Chrome).
+If multiple arguments are returned from the async function, `console.dir` is
called on each argument in order.
__Arguments__
-* function - The function you want to eventually apply all arguments to.
-* arguments... - Any number of arguments to apply to the function.
+* `function` - The function you want to eventually apply all arguments to.
+* `arguments...` - Any number of arguments to apply to the function.
__Example__
@@ -1413,5 +1560,5 @@ node> async.dir(hello, 'world');
<a name="noConflict" />
### noConflict()
-Changes the value of async back to its original value, returning a reference to the
-async object.
+Changes the value of `async` back to its original value, returning a reference to the
+`async` object.
diff --git a/component.json b/component.json
new file mode 100644
index 0000000..bbb0115
--- /dev/null
+++ b/component.json
@@ -0,0 +1,11 @@
+{
+ "name": "async",
+ "repo": "caolan/async",
+ "description": "Higher-order functions and common patterns for asynchronous code",
+ "version": "0.1.23",
+ "keywords": [],
+ "dependencies": {},
+ "development": {},
+ "main": "lib/async.js",
+ "scripts": [ "lib/async.js" ]
+}
diff --git a/lib/async.js b/lib/async.js
index 14d20cc..04f7246 100755
--- a/lib/async.js
+++ b/lib/async.js
@@ -1,3 +1,4 @@
+/*jshint onevar: false, indent:4 */
/*global setImmediate: false, setTimeout: false, console: false */
(function () {
@@ -27,6 +28,10 @@
//// cross-browser compatiblity functions ////
+ var _isArray = Array.isArray || function (obj) {
+ return toString.call(obj) === '[object Array]';
+ };
+
var _each = function (arr, iterator) {
if (arr.forEach) {
return arr.forEach(iterator);
@@ -75,19 +80,30 @@
//// nextTick implementation with browser-compatible fallback ////
if (typeof process === 'undefined' || !(process.nextTick)) {
if (typeof setImmediate === 'function') {
- async.setImmediate = setImmediate;
- async.nextTick = setImmediate;
+ async.nextTick = function (fn) {
+ // not a direct alias for IE10 compatibility
+ setImmediate(fn);
+ };
+ async.setImmediate = async.nextTick;
}
else {
- async.setImmediate = async.nextTick;
async.nextTick = function (fn) {
setTimeout(fn, 0);
};
+ async.setImmediate = async.nextTick;
}
}
else {
async.nextTick = process.nextTick;
- async.setImmediate = setImmediate;
+ if (typeof setImmediate !== 'undefined') {
+ async.setImmediate = function (fn) {
+ // not a direct alias for IE10 compatibility
+ setImmediate(fn);
+ };
+ }
+ else {
+ async.setImmediate = async.nextTick;
+ }
}
async.each = function (arr, iterator, callback) {
@@ -97,19 +113,20 @@
}
var completed = 0;
_each(arr, function (x) {
- iterator(x, only_once(function (err) {
- if (err) {
- callback(err);
- callback = function () {};
- }
- else {
- completed += 1;
- if (completed >= arr.length) {
- callback(null);
- }
- }
- }));
+ iterator(x, only_once(done) );
});
+ function done(err) {
+ if (err) {
+ callback(err);
+ callback = function () {};
+ }
+ else {
+ completed += 1;
+ if (completed >= arr.length) {
+ callback(null);
+ }
+ }
+ }
};
async.forEach = async.each;
@@ -411,8 +428,11 @@
addListener(function () {
if (_keys(results).length === keys.length) {
- callback(null, results);
+ var theCallback = callback;
+ // prevent final callback from calling itself if it errors
callback = function () {};
+
+ theCallback(null, results);
}
});
@@ -461,7 +481,7 @@
async.waterfall = function (tasks, callback) {
callback = callback || function () {};
- if (tasks.constructor !== Array) {
+ if (!_isArray(tasks)) {
var err = new Error('First argument to waterfall must be an array of functions');
return callback(err);
}
@@ -494,7 +514,7 @@
var _parallel = function(eachfn, tasks, callback) {
callback = callback || function () {};
- if (tasks.constructor === Array) {
+ if (_isArray(tasks)) {
eachfn.map(tasks, function (fn, callback) {
if (fn) {
fn(function (err) {
@@ -534,7 +554,7 @@
async.series = function (tasks, callback) {
callback = callback || function () {};
- if (tasks.constructor === Array) {
+ if (_isArray(tasks)) {
async.mapSeries(tasks, function (fn, callback) {
if (fn) {
fn(function (err) {
@@ -622,7 +642,8 @@
if (err) {
return callback(err);
}
- if (test()) {
+ var args = Array.prototype.slice.call(arguments, 1);
+ if (test.apply(null, args)) {
async.doWhilst(iterator, test, callback);
}
else {
@@ -650,7 +671,8 @@
if (err) {
return callback(err);
}
- if (!test()) {
+ var args = Array.prototype.slice.call(arguments, 1);
+ if (!test.apply(null, args)) {
async.doUntil(iterator, test, callback);
}
else {
@@ -664,9 +686,17 @@
concurrency = 1;
}
function _insert(q, data, pos, callback) {
- if(data.constructor !== Array) {
+ 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,
@@ -740,8 +770,9 @@
saturated: null,
empty: null,
drain: null,
+ drained: true,
push: function (data, callback) {
- if(data.constructor !== Array) {
+ if (!_isArray(data)) {
data = [data];
}
_each(data, function(task) {
@@ -749,6 +780,7 @@
data: task,
callback: typeof callback === 'function' ? callback : null
});
+ cargo.drained = false;
if (cargo.saturated && tasks.length === payload) {
cargo.saturated();
}
@@ -758,13 +790,14 @@
process: function process() {
if (working) return;
if (tasks.length === 0) {
- if(cargo.drain) cargo.drain();
+ if(cargo.drain && !cargo.drained) cargo.drain();
+ cargo.drained = true;
return;
}
var ts = typeof payload === 'number'
? tasks.splice(0, payload)
- : tasks.splice(0);
+ : tasks.splice(0, tasks.length);
var ds = _map(ts, function (task) {
return task.data;
@@ -832,7 +865,9 @@
var callback = args.pop();
var key = hasher.apply(null, args);
if (key in memo) {
- callback.apply(null, memo[key]);
+ async.nextTick(function () {
+ callback.apply(null, memo[key]);
+ });
}
else if (key in queues) {
queues[key].push(callback);
@@ -876,8 +911,8 @@
return async.mapSeries(counter, iterator, callback);
};
- async.compose = function (/* functions... */) {
- var fns = Array.prototype.reverse.call(arguments);
+ async.seq = function (/* functions... */) {
+ var fns = arguments;
return function () {
var that = this;
var args = Array.prototype.slice.call(arguments);
@@ -895,6 +930,10 @@
};
};
+ async.compose = function (/* functions... */) {
+ return async.seq.apply(null, Array.prototype.reverse.call(arguments));
+ };
+
var _applyEach = function (eachfn, fns /*args...*/) {
var go = function () {
var that = this;
@@ -929,16 +968,16 @@
next();
};
+ // Node.js
+ if (typeof module !== 'undefined' && module.exports) {
+ module.exports = async;
+ }
// AMD / RequireJS
- if (typeof define !== 'undefined' && define.amd) {
+ else if (typeof define !== 'undefined' && define.amd) {
define([], function () {
return async;
});
}
- // Node.js
- else if (typeof module !== 'undefined' && module.exports) {
- module.exports = async;
- }
// included directly via <script> tag
else {
root.async = async;
diff --git a/package.json b/package.json
index 9fa85b5..e0baa66 100644
--- a/package.json
+++ b/package.json
@@ -3,18 +3,18 @@
"description": "Higher-order functions and common patterns for asynchronous code",
"main": "./lib/async",
"author": "Caolan McMahon",
- "version": "0.2.6",
+ "version": "0.2.10",
"repository" : {
"type" : "git",
- "url" : "http://github.com/caolan/async.git"
+ "url" : "https://github.com/caolan/async.git"
},
"bugs" : {
- "url" : "http://github.com/caolan/async/issues"
+ "url" : "https://github.com/caolan/async/issues"
},
"licenses" : [
{
"type" : "MIT",
- "url" : "http://github.com/caolan/async/raw/master/LICENSE"
+ "url" : "https://github.com/caolan/async/raw/master/LICENSE"
}
],
"devDependencies": {
diff --git a/test/test-async.js b/test/test-async.js
index ff401e7..fdcac5f 100755
--- a/test/test-async.js
+++ b/test/test-async.js
@@ -261,6 +261,93 @@ 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');
+ var testcontext = {name: 'foo'};
+
+ var add2 = function (n, cb) {
+ test.equal(this, testcontext);
+ setTimeout(function () {
+ cb(null, n + 2);
+ }, 50);
+ };
+ var mul3 = function (n, cb) {
+ test.equal(this, testcontext);
+ setTimeout(function () {
+ cb(null, n * 3);
+ }, 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();
+ });
+};
+
exports['auto'] = function(test){
var callOrder = [];
var testdata = [{test: 'test'}];
@@ -431,6 +518,42 @@ exports['auto removeListener has side effect on loop iterator'] = function(test)
});
};
+// Issue 410 on github: https://github.com/caolan/async/issues/410
+exports['auto calls callback multiple times'] = function(test) {
+ if (typeof process === 'undefined') {
+ // node only test
+ return;
+ }
+ var finalCallCount = 0;
+ var domain = require('domain').create();
+ domain.on('error', function (e) {
+ // ignore test error
+ if (!e._test_error) {
+ return test.done(e);
+ }
+ });
+ domain.run(function () {
+ async.auto({
+ task1: function(callback) { callback(null); },
+ task2: ['task1', function(callback) { callback(null); }]
+ },
+
+ // Error throwing final callback. This should only run once
+ function(err) {
+ finalCallCount++;
+ var e = new Error("An error");
+ e._test_error = true;
+ throw e;
+ });
+ });
+ setTimeout(function () {
+ test.equal(finalCallCount, 1,
+ "Final auto callback should only be called once"
+ );
+ test.done();
+ }, 10);
+};
+
exports['waterfall'] = function(test){
test.expect(6);
var call_order = [];
@@ -1711,6 +1834,34 @@ exports['doUntil'] = function (test) {
);
};
+exports['doUntil callback params'] = function (test) {
+ var call_order = [];
+ var count = 0;
+ async.doUntil(
+ function (cb) {
+ debugger
+ call_order.push(['iterator', count]);
+ count++;
+ cb(null, count);
+ },
+ function (c) {
+ call_order.push(['test', c]);
+ return (c == 5);
+ },
+ function (err) {
+ test.same(call_order, [
+ ['iterator', 0], ['test', 1],
+ ['iterator', 1], ['test', 2],
+ ['iterator', 2], ['test', 3],
+ ['iterator', 3], ['test', 4],
+ ['iterator', 4], ['test', 5]
+ ]);
+ test.equals(count, 5);
+ test.done();
+ }
+ );
+};
+
exports['whilst'] = function (test) {
var call_order = [];
@@ -1769,6 +1920,35 @@ exports['doWhilst'] = function (test) {
);
};
+exports['doWhilst callback params'] = function (test) {
+ var call_order = [];
+
+ var count = 0;
+ async.doWhilst(
+ function (cb) {
+ call_order.push(['iterator', count]);
+ count++;
+ cb(null, count);
+ },
+ function (c) {
+ call_order.push(['test', c]);
+ return (c < 5);
+ },
+ function (err) {
+ debugger
+ test.same(call_order, [
+ ['iterator', 0], ['test', 1],
+ ['iterator', 1], ['test', 2],
+ ['iterator', 2], ['test', 3],
+ ['iterator', 3], ['test', 4],
+ ['iterator', 4], ['test', 5]
+ ]);
+ test.equals(count, 5);
+ test.done();
+ }
+ );
+};
+
exports['queue'] = function (test) {
var call_order = [],
delays = [160,80,240,80];
@@ -2195,28 +2375,115 @@ exports['cargo bulk task'] = function (test) {
}, 800);
};
+exports['cargo drain once'] = function (test) {
+
+ var c = async.cargo(function (tasks, callback) {
+ callback();
+ }, 3);
+
+ var drainCounter = 0;
+ c.drain = function () {
+ drainCounter++;
+ }
+
+ for(var i = 0; i < 10; i++){
+ c.push(i);
+ }
+
+ setTimeout(function(){
+ test.equal(drainCounter, 1);
+ test.done();
+ }, 500);
+};
+
+exports['cargo drain twice'] = function (test) {
+
+ var c = async.cargo(function (tasks, callback) {
+ callback();
+ }, 3);
+
+ var loadCargo = function(){
+ for(var i = 0; i < 10; i++){
+ c.push(i);
+ }
+ };
+
+ var drainCounter = 0;
+ c.drain = function () {
+ drainCounter++;
+ }
+
+ loadCargo();
+ setTimeout(loadCargo, 500);
+
+ setTimeout(function(){
+ test.equal(drainCounter, 2);
+ test.done();
+ }, 1000);
+};
+
exports['memoize'] = function (test) {
test.expect(4);
var call_order = [];
var fn = function (arg1, arg2, callback) {
- call_order.push(['fn', arg1, arg2]);
- callback(null, arg1 + arg2);
+ async.setImmediate(function () {
+ call_order.push(['fn', arg1, arg2]);
+ callback(null, arg1 + arg2);
+ });
};
var fn2 = async.memoize(fn);
fn2(1, 2, function (err, result) {
test.equal(result, 3);
+ fn2(1, 2, function (err, result) {
+ test.equal(result, 3);
+ fn2(2, 2, function (err, result) {
+ test.equal(result, 4);
+ test.same(call_order, [['fn',1,2], ['fn',2,2]]);
+ test.done();
+ });
+ });
});
+};
+
+exports['memoize maintains asynchrony'] = function (test) {
+ test.expect(3);
+ var call_order = [];
+
+ var fn = function (arg1, arg2, callback) {
+ call_order.push(['fn', arg1, arg2]);
+ async.setImmediate(function () {
+ call_order.push(['cb', arg1, arg2]);
+ callback(null, arg1 + arg2);
+ });
+ };
+
+ var fn2 = async.memoize(fn);
fn2(1, 2, function (err, result) {
test.equal(result, 3);
- });
- fn2(2, 2, function (err, result) {
- test.equal(result, 4);
- });
-
- test.same(call_order, [['fn',1,2], ['fn',2,2]]);
- test.done();
+ fn2(1, 2, function (err, result) {
+ test.equal(result, 3);
+ async.nextTick(memoize_done);
+ call_order.push('tick3');
+ });
+ call_order.push('tick2');
+ });
+ call_order.push('tick1');
+
+ function memoize_done() {
+ var async_call_order = [
+ ['fn',1,2], // initial async call
+ 'tick1', // async caller
+ ['cb',1,2], // async callback
+ // ['fn',1,2], // memoized // memoized async body
+ 'tick2', // handler for first async call
+ // ['cb',1,2], // memoized // memoized async response body
+ 'tick3' // handler for memoized async call
+ ];
+ test.same(call_order, async_call_order);
+ test.done();
+ }
};
exports['unmemoize'] = function(test) {
@@ -2225,24 +2492,24 @@ exports['unmemoize'] = function(test) {
var fn = function (arg1, arg2, callback) {
call_order.push(['fn', arg1, arg2]);
- callback(null, arg1 + arg2);
+ async.setImmediate(function () {
+ callback(null, arg1 + arg2);
+ });
};
var fn2 = async.memoize(fn);
var fn3 = async.unmemoize(fn2);
fn3(1, 2, function (err, result) {
test.equal(result, 3);
+ fn3(1, 2, function (err, result) {
+ test.equal(result, 3);
+ fn3(2, 2, function (err, result) {
+ test.equal(result, 4);
+ test.same(call_order, [['fn',1,2], ['fn',1,2], ['fn',2,2]]);
+ test.done();
+ });
+ });
});
- fn3(1, 2, function (err, result) {
- test.equal(result, 3);
- });
- fn3(2, 2, function (err, result) {
- test.equal(result, 4);
- });
-
- test.same(call_order, [['fn',1,2], ['fn',1,2], ['fn',2,2]]);
-
- test.done();
}
exports['unmemoize a not memoized function'] = function(test) {
@@ -2302,11 +2569,11 @@ exports['memoize custom hash function'] = function (test) {
});
fn2(1, 2, function (err, result) {
test.equal(result, 3);
+ fn2(2, 2, function (err, result) {
+ test.equal(result, 3);
+ test.done();
+ });
});
- fn2(2, 2, function (err, result) {
- test.equal(result, 3);
- });
- test.done();
};
exports['memoize manually added memo value'] = function (test) {
@@ -2436,3 +2703,25 @@ exports['queue events'] = function(test) {
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);
+
+ 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([]);
+};