The first pattern we looked at in this series was the Node.js callback pattern. As I mentioned there, that pattern alone will only get you so far. Eventually, you’ll want to construct asynchronous workflows that process elements in a collection serially or run several tasks in parallel. You could write your own library, but why reinvent the wheel when you could just use Async, one of the most popular Node.js libraries ever. In this post, we’ll take a look at Async to see how it can help you write asynchronous code in Node.js.

Contents:

Async module overview

Async is not included with Node.js so it must be installed via NPM (Yarn and Bower work too) using a command like npm install async –save. A native means to reason about async processing, Promise, eventually made its way into JavaScript and Node.js. We’ll cover promises in the next part of this series. For now, let’s focus on Async.

Once installed, you can require the library in and take advantage of its 70+ methods for various asynchronous processing situations. A glance at the documentation shows that Async’s methods fall into three main groups: Collections, Control Flow, and Utils. Let’s look at a couple examples from the first two groups.

Collections

This first example uses Async’s eachSeries method to process an array one element at a time.

Here’s an overview of the code above:

  • Line 1: The Async library is required in after having been installed.
  • Lines 3-14: A fake API that implements the Node.js callback pattern is defined. The API will occasionally simulate an error occurring so that behavior can be observed.
  • Lines 16-20: An array of “things” to process is defined. Typically, the elements would be obtained from reading a file or querying a database. Each element needs to be processed using the fake API.
  • Lines 22-34: Async’s eachSeries method is used the process the array. The first parameter is the array to be processed, the second parameter is a function (iteratee) that will process a single element, and the third parameter is a function that should be invoked when all work is done or immediately after an error occurs. Async will pass the iteratee one element from the array along with a callback that, when invoked, will let Async know it can move to the next element in the array.

Copy the script to a file and run it with Node.js (don’t forget to install Async). You should see that each element is processed serially. If an error occurs, processing will stop and the final callback will be invoked immediately.

If you change the eachSeries method to each, you’ll see that all elements are processed in parallel. Using eachLimit (requires an additional parameter) will limit the number of functions running at the same time. Pretty cool, huh?

Control flow

Here’s an example of a Control Flow method, series, which takes an array of functions and executes them one at a time.

Here’s a rundown of the script above:

  • Line 1: The Async library is required in after having been installed.
  • Lines 3-14: A fake API that implements the Node.js callback pattern is defined. The API will occasionally simulate an error occurring so that behavior can be observed.
  • Lines 16-37: Async’s series method is used to process a number of asynchronous tasks/functions. The first parameter is the arrays of functions, each of which is passed a callback used to indicate when its work is complete. The second parameter is a function that will be invoked when all tasks have completed or immediately after an error occurs.

If you copy the script to a file and run it with Node.js, you’ll see that it behaves very much like the first script, only it works with tasks rather than elements in an array. If you replace series with parallel the tasks will run at the same time.

As you can see, Async extends the Node.js callback pattern, adding many handy methods that make quick work of various asynchronous flows. The library includes many more methods that can help with specific use cases, such as waterfall, queue, and race. Try to be aware of what’s avaialble so you can leverage it if needed.

Async module demo app

The Async demo app is comprised of the following four files. The files are also available via this Gist.

This is a very basic package.json file. Note that both async and oracledb are listed as depenencies.

In this version of the index.js, Async’s series method is used to first create a connection pool and then to fetch an employee. Although the pool is passed to the callback function for createPool, it’s not referenced here as the built-in pool cache will be used in employees.js.

The db-config.js file is used in index.js to provide the connection info for the database. This configuration should work with the DB App Dev VM, but it will need to be adjusted for other environments.

This version of the employees.js file uses Async’s waterfall method to get a connection to the database, use it to execute a query, and then close a connection. I chose waterfall over, say, series because it allows me to pass the connection obtained in the first task along to the next task. There are other ways to manage state with series.

The logic to close the connection was put in the final function (not in the list of tasks), as that will be invoked if the tasks complete successfully or if an error occurs – much like finally in try…catch…finally.

Hopefully, you now see how powerful the Async module can be. Check out the next part of the series to see how to use promises to do the same work.

Leave a Reply