For Async v1.5.x documentation, go HERE
Async is a utility module which provides straight-forward, powerful functions
for working with asynchronous JavaScript. Although originally designed for
use with Node.js and installable via npm install --save async
,
it can also be used directly in the browser.
Async is also installable via:
Async provides around 70 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 asynchronous function -- a callback which expects an Error as its first argument -- and calling the callback once.
Quick Examples
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'], function(filePath, callback) {
fs.access(filePath, function(err) {
callback(null, !err)
});
}, function(err, results) {
// results now equals an array of the existing files
});
async.parallel([
function(callback) { ... },
function(callback) { ... }
], function(err, results) {
// optional callback
});
async.series([
function(callback) { ... },
function(callback) { ... }
]);
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 (StackOverflow)
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 iteratee. 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:
async.eachSeries(hugeArray, function iteratee(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:
async.eachSeries(hugeArray, function iteratee(item, callback) {
if (inCache(item)) {
async.setImmediate(function() {
callback(null, cache[item]);
});
} else {
doSomeIO(item, callback);
//...
}
});
Async does not guard against synchronous iteratees for performance reasons. If you are still running into stack overflows, you can defer as suggested above, or wrap functions with async.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 or this talk 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.
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.
Using ES2017 async
functions
Async accepts async
functions wherever we accept a Node-style callback function. However, we do not pass them a callback, and instead use the return value and handle any promise rejections or errors thrown.
async.mapLimit(files, async file => { // <- no callback!
const text = await util.promisify(fs.readFile)(dir + file, 'utf8')
const body = JSON.parse(text) // <- a parse error herre will be caught automatically
if (!(await checkValidity(body))) {
throw new Error(`${file} has invalid contents`) // <- this error will also be caught
}
return body // <- return a value!
}, (err, contents) => {
if (err) throw err
console.log(contents)
})
We can only detect native async
functions, not transpiled versions (e.g. with Babel). Otherwise, you can wrap async
functions in async.asyncify()
.
Binding a context to an iteratee
This section is really about bind
, not about Async. If you are wondering how to
make Async execute your iteratees in a given context, or are confused as to why
a method of another library isn't working as an iteratee, study this example:
// Here is a simple object with an (unnecessarily roundabout) squaring method
var AsyncSquaringLibrary = {
squareExponent: 2,
square: function(number, callback){
var result = Math.pow(number, this.squareExponent);
setTimeout(function(){
callback(null, result);
}, 200);
}
};
async.map([1, 2, 3], AsyncSquaringLibrary.square, function(err, result) {
// result is [NaN, NaN, NaN]
// This fails because the `this.squareExponent` expression in the square
// function is not evaluated in the context of AsyncSquaringLibrary, and is
// therefore undefined.
});
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 iteratee before
// 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.
});
Download
The source is available for download from GitHub. Alternatively, you can install using npm:
$ npm install --save async
As well as using Bower:
$ bower install async
You can then require()
async as normal:
var async = require("async");
Or require individual methods:
var waterfall = require("async/waterfall");
var map = require("async/map");
Development: async.js - 29.6kb Uncompressed
In the Browser
Async should work in any ES5 environment (IE9 and above).
Usage:
<script type="text/javascript" src="async.js"></script>
<script type="text/javascript">
async.map(data, asyncProcess, function(err, results) {
alert(results);
});
</script>
The portable versions of Async, including async.js
and async.min.js
, are
included in the /dist
folder. Async can also be found on the jsDelivr CDN.
ES Modules
We also provide Async as a collection of ES2015 modules, in an alternative async-es
package on npm.
$ npm install --save async-es
import waterfall from 'async-es/waterfall';
import async from 'async-es';
Other Libraries
limiter
a package for rate-limiting based on requests per sec/hour.neo-async
an altername implementation of Async, focusing on speed.co-async
a library inspired by Async for use withco
and generator functions.promise-async
a version of Async where all the methods are Promisified.