- Asynchronous Programming with Node.js
- Using the Async Module
- Conclusion
Using the Async Module
The Async module has a method called series() that allows us to control the execution flow of callbacks. The series() method takes an array of functions as an argument (or an object that contains the functions). The functions are executed in the order in which they exist in the array. Each callback function in the list takes the err parameter as the first argument to the function, and the second argument is the return value (if there is one).
The series() function also accepts a second argument, a callback function that contains the results of the functions passed in as the first parameter.
To demonstrate how the series() method can clean up code, the following example has three timer tasks. Each task executes as a callback within the other, all the way to the top or to the first timer task. The code looks like this:
var results = []; setTimeout(function() { console.log(“Task 1”); results[0] = 1; setTimeout(function() { console.log(“Task 2”); results[1] = 2; setTimeout(function() { console.log(“Task 3”); results[2] = 3; }, 100); }, 200); }, 300);
It looks a bit complicated and messy. The example below shows the same code rewritten using the async.series() function:
var async = require(“async”); async.series([ function(callback) { setTimeout(function() { console.log(“Task 1”); callback(null, 1); }, 300); }, function(callback) { setTimeout(function() { console.log(“Task 2”); callback(null, 2); }, 200); }, function(callback) { setTimeout(function() { console.log(“Task 3“); callback(null, 3); }, 100); } ], function(error, results) { console.log(results); });
Nice improvement. Now the code is sequential in execution, but is actually being executed asynchronously (within each callback) using the async.series() function. The async.series() function is useful if you need to execute a set of async functions in a certain order.
You can also use the async.parallel() method to perform parallel-like executions of your callbacks. The difference between them is that series() waits for one function to finish before executing the next one; parallel() does not.
If we use the previous example but change it to parallel(), the code looks like this:
var async = require(“async”); async.series([ function(callback) { setTimeout(function() { console.log(“Task 1”); callback(null, 1); }, 300); }, function(callback) { setTimeout(function() { console.log(“Task 2”); callback(null, 2); }, 200); }, function(callback) { setTimeout(function() { console.log(“Task 3”); callback(null, 3); }, 100); } ], function(error, results) { console.log(results); });
Executing the code above should now result in Task 3 finishing first, then Task 2, and finally Task 1. Using parallel() is how you would expect the code to behave when using async callbacks; series makes sure the callbacks are executed in order, regardless of timers and so on.
What if you need to execute part of your callbacks sequentially in a series by limiting those other functions to be executed in parallel? You can use the parallel.Limit() method, which takes an extra argument to specify the positional limit of the last function to be executed in parallel:
var async = require(“async”); async.parallelLimit([ function(callback) { setTimeout(function() { console.log(“Task 1”); callback(null, 1); }, 300); }, function(callback) { setTimeout(function() { console.log(“Task 2”); callback(null, 2); }, 200); }, function(callback) { setTimeout(function() { console.log(“Task 3”); callback(null, 3); }, 100); } ], 2, function(error, results) { console.log(results); });
After executing this code, you should see that Task 2 finishes first, followed by Task 1, and then finally Task 3 (which was not included in the parallel tasking because it was position 3 in the function array).
The async.waterfall() method allows you to pass values between the functions in a trickle-down manner. The waterfall() method is useful when you need the results of a previous function to perform an operation with the next function in the series. An example appears below:
var async = require(“async”); async.waterfall([ function(callback) { callback(null, 12, 15); }, function(a, b, callback) { callback(null, (a + b) * 10); }, function(cc, callback) { callback(null, Math.sqrt(cc)); } ], function(error, c) { console.log(c); });
In this example, the numbers 12 and 15 were added, which was handled by the second function. The result of this addition is passed into the next function as the parameter cc, which applied to the sqrt function. The final result is the square root of (12 + 15) * 10, which is passed out using the parameter c to the waterfall() method’s final callback function.
Notice that the waterfall() method allows you to pass in extra parameters to callback functions, not just the err and return value as with the series() method.