An Async function is a function declared with the async keyword.
Async functions are instances of the AsyncFunction constructor, and the await keyword is permitted within them.
Async is a utility module that provides straight-forward, powerful functions for working with asynchronous JavaScript. Although originally designed for use with Node.js and installable via npm i async, it can also be used directly in the browser.
The async and await keywords enable asynchronous, promise-based behavior to be written in a cleaner style, avoiding the need to explicitly configure promise chains.
Syntax
Each callback must be written with this syntax:
function callback(err, result [, arg1[, ...]])
This way, you are forced to return the error first, and can't ignore handling them later on. null is the convention in the absence of errors
callback(null, myResult);
Your callbacks can contain more arguments than err and result, but it's useful only for a specific set of functions (waterfall, seq, ...)
callback(null, myResult, myCustomArgument);
And, of course, send errors. You must do it, and handle errors (or at least log them).
callback(err);
Examples :
Parallel: multi-tasking
async.parallel(tasks, afterTasksCallback) will execute a set of tasks in parallel and wait for the end of all tasks (reported by the call of callback function).
When tasks are finished, async call the main callback with all errors and all results of tasks.
function shortTimeFunction(callback) { setTimeout(function() { callback(null, 'resultOfShortTime'); }, 200); } function mediumTimeFunction(callback) { setTimeout(function() { callback(null, 'resultOfMediumTime'); }, 500); } function longTimeFunction(callback) { setTimeout(function() { callback(null, 'resultOfLongTime'); }, 1000); }
async.parallel( [ shortTimeFunction, mediumTimeFunction, longTimeFunction ], function(err, results) { if (err) { return console.error(err); } console.log(results); } );
Result :
["resultOfShortTime", "resultOfMediumTime", "resultOfLongTime"]
Call async.parallel() with an object
You can replace the tasks array parameter by an object. In this case, results will be also an object with the same keys as tasks.
It's very useful to compute some tasks and find easily each result.
async.parallel({ short: shortTimeFunction, medium: mediumTimeFunction, long: longTimeFunction }, function(err, results) { if (err) { return console.error(err); } console.log(results); } );
Result :
{short: "resultOfShortTime", medium: "resultOfMediumTime", long: "resultOfLongTime"}
Resolving multiple values
Each parallel function is passed a callback. This callback can either return an error as the first argument or success values after that. If a callback is passed several success values, these results are returned as an array.
async.parallel({ short: function shortTimeFunction(callback) { setTimeout(function() { callback(null, 'resultOfShortTime1', 'resultOfShortTime2'); }, 200); }, medium: function mediumTimeFunction(callback) { setTimeout(function() { callback(null, 'resultOfMediumTime1', 'resultOfMeiumTime2'); }, 500); } }, function(err, results) { if (err) { return console.error(err); } console.log(results); } );
Result :
{
short: ["resultOfShortTime1", "resultOfShortTime2"],
medium: ["resultOfMediumTime1", "resultOfMediumTime2"]
}
Series: independent mono-tasking
async.series(tasks, afterTasksCallback) will execute a set of tasks. Each task is executed after another. If a task fails, async stops immediately the execution and jump into the main callback.
When tasks are finished successfully, async call the "master" callback with all errors and all results of tasks.
function shortTimeFunction(callback) {
setTimeout(function() {
callback(null, 'resultOfShortTime');
}, 200);
}
function mediumTimeFunction(callback) {
setTimeout(function() {
callback(null, 'resultOfMediumTime');
}, 500);
}
function longTimeFunction(callback) {
setTimeout(function() {
callback(null, 'resultOfLongTime');
}, 1000);
}
async.series([
mediumTimeFunction,
shortTimeFunction,
longTimeFunction
],
function(err, results) {
if (err) {
return console.error(err);
}
console.log(results);
}
);
Result:
["resultOfMediumTime", "resultOfShortTime", "resultOfLongTime"]
Call async.series() with an object
You can replace the tasks array parameter by an object. In this case, results will be also an object with the same keys as tasks.
It's very useful to compute some tasks and find easily each result.
async.series({
short: shortTimeFunction,
medium: mediumTimeFunction,
long: longTimeFunction
}, function(err, results) {
if (err) {
return console.error(err);
}
console.log(results);
});
Result :
{
short: "resultOfShortTime",
medium: "resultOfMediumTime",
long: "resultOfLongTime"
}
Waterfall: dependent mono-tasking
async.waterfall(tasks, afterTasksCallback) will execute a set of tasks. Each task is executed after another, and the result of a task is passed to the next task. As async.series(), if a task fails, async stop the execution and call immediately the main callback.
When tasks are finished successfully, async call the "master" callback with all errors and all results of tasks.
function getUserRequest(callback) {
// We simulate the request with a timeout
setTimeout(function() {
var userResult = { name : 'Priyal' };
callback(null, userResult);
}, 500);
}
function getUserFriendsRequest(user, callback) {
// Another request simulate with a timeout
setTimeout(function() {
var friendsResult = [];
if (user.name === "Priyal"){
friendsResult = [
{ name : 'Alice' },
{ name: 'Bob' }
];
}
callback(null, friendsResult);
}, 500);
}
async.waterfall([
getUserRequest,
getUserFriendsRequest
],
function(err, results) {
if (err) {
return console.error(err);
}
console.log(JSON.stringify(results));
}
);
Result:
results contain the second callback parameter of the last function of the waterfall, which is friendsResult in that case.
async.times(To handle for loop in a better way)
To execute a function within a loop in node.js, it's fine to use a for loop for short loops. But the loop is long, using for loop will increase the time of processing which might cause the node process to hang. In such scenarios, you can use: asycn.times
function recursiveAction(n, callback) {
//do whatever want to do repeatedly
callback(err, result);
}
async.times(5, function(n, next) {
recursiveAction(n,
function(err, result) {
next(err, result);
}
);
}, function(err, results) {
// we should now have 5 result
});
This is called in parallel. When we want to call it one at a time, use: async.timesSeries
async.each(To handle an array of data efficiently)
When we want to handle an array of data, it's better to use async.each. When we want to perform something with all data & want to get the final callback once everything is done, then this method will be useful. This is handled in a parallel way.
function createUser(userName, callback) {
//create user in db
callback(null)//or error based on creation
}
var arrayOfData = ['Ritu', 'Sid', 'Tom'];
async.each(arrayOfData, function(eachUserName, callback) {
// Perform operation on each user.
console.log('Creating user '+eachUserName);
//Returning callback is must. Else it wont get the final callback, even if we miss to return one callback
createUser(eachUserName, callback);
}, function(err) {
//If any of the user creation failed may throw error.
if( err ) {
// One of the iterations produced an error.
// All processing will now stop.
console.log('unable to create user');
} else {
console.log('All user created successfully'); }
});
To do one at a time can use async.eachSeries
async.series(To handle events one by one)
In async.series, all the functions are executed in series and the consolidated outputs of each function are passed to the final callback. e.g
var async = require('async');
async.series([
function (callback) {
console.log('First Execute..');
callback(null, 'userPersonalData');
},
function (callback) {
console.log('Second Execute.. ');
callback(null, 'userDependentData');
}
], function (err, result) {
console.log(result);
});
//Output:
First Execute.. Second Execute.. ['userPersonalData','userDependentData']
Very nice article ...
ReplyDelete