Async.js’s collection methods solve the problem of applying a single async function to a set of data. But what if, instead of a set of data, we have a set of functions? In this section, we’ll explore some of the powerful tools that Async.js has for dispatching async functions and collecting their results.
Suppose we have an array of async functions that we want to run in order. Without the use of a utility function, we might have to write something like this:
| funcs[0](function() { |
| funcs[1](function() { |
| funcs[2](onComplete); |
| }) |
| }); |
Fortunately, we have async.series and async.waterfall. Each takes an array of functions (the task list) and runs them sequentially, passing each one a Node-style callback. The difference between the two is that async.series provides only the callback to each task, whereas async.waterfall also provides the results from the previous task. (By “results,” I mean the nonerror values each task passes to its callback.)
Let’s look at a simple demonstration using timeouts.
Asyncjs/seriesTimers.js | |
| var async = require ('async'); |
| |
| var start = new Date; |
| |
| async.series([ |
| function(callback) { setTimeout(callback, 100); }, |
| function(callback) { setTimeout(callback, 300); }, |
| function(callback) { setTimeout(callback, 200); } |
| ], function(err, results) { |
| // show time elapsed since start |
| console.log('Completed in ' + (new Date - start) + 'ms'); |
| }); |
(Substituting async.waterfall for async.series would have no effect on this example, since each task’s callback is run with no arguments.)
The completion handler will run after a little over 600m because each task in the array is completed in order. The callback that Async.js passes to each task function simply asks, “Is there an error (the first argument)? If not, then I’ll collect the result (the second argument) and run the next task.”
The next time you have a set of async functions that you want to run sequentially, reach for async.series or async.waterfall. There’s an excellent chance that one of them is the right tool for the job.
Async.js offers a parallel analog of async.series called async.parallel. Just like async.series, it takes an array of functions of the form function(callback) {...}, plus an (optional) completion handler that runs after the last callback fires.
Let’s repeat our timeout example.
Asyncjs/parallelTimers.js | |
| var async = require ('async'); |
| var start = new Date; |
| async.parallel([ |
| function(callback) { setTimeout(callback, 100); }, |
| function(callback) { setTimeout(callback, 300); }, |
| function(callback) { setTimeout(callback, 200); } |
| ], function(err, results) { |
| console.log('Completed in ' + (new Date - start) + 'ms'); |
| }); |
Whereas async.series took the sum of the timeouts to complete (~600ms), async.parallel takes only the max timeout (~300ms).
Conveniently, Async.js passes the results to the completion handler in the order corresponding to the task array, not the order in which the results were generated. Thus, you get the performance benefits of parallelism without the unpredictability.
Along with the collection methods, async.series, async.waterfall, and async.parallel are the heart and soul of Async.js: simple, time-saving utility functions for the most common async scenarios.
3.144.95.36