02 – Exploring Async.js – async.waterfall and async.series

Share:
Source Code

In “Exploring async.js”, we’re going to learn about async.js, the jQuery of Nodejs.

Async.js 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 async, it can also be used directly in the browser.

https://github.com/caolan/async

This is the second instalment in a series of tutorials where we go over some of the most useful functions from async.js.  If you need a refresher on how to set up the project so you can follow along, please read this first.

async.waterfall

async.waterfall(tasks, [callback])

Runs the tasks array of functions in series, each passing their results to the next in the array. However, if any of the tasks pass an error to their own callback, the next function is not executed, and the main callback is immediately called with the error.

Arguments

  • tasks – An array of functions to run, each function is passed a callback(err, result1, result2, ...) it must call on completion. The first argument is an error (which can be null) and any further arguments will be passed as arguments in order to the next task.
  • callback(err, [results]) – An optional callback to run once all the functions have completed. This will be passed the results of the last task’s callback.

We’ll simulate a real world example where we need to make a function call to Github’s API to retrieve user’s avatar.

Here’s the code sample we’ll use to demonstrate:

var async = require('async');
var GithubApi = require('github');

var github = new GithubApi({
    	version: '3.0.0'
});

async.waterfall([
    	function getUserAvatar(callback) {
        		github.search.users({ q: 'airbnb' }, function(err, res) {
			            if (err) {
				                callback(err, null);
                				return;
           			}

           			var avatarUrl = res.items[0].avatar_url;
			           callback(null, avatarUrl);
        		});
    	},
	    function wrapAvatarInHtml(avatarUrl, callback) {
        		var avatarWithHtml = '<img src="' + avatarUrl + '"/>';
        		callback(null, avatarWithHtml);
    	}
], function(err, result) {
    	if (err) {
        		console.error(err);
        		return;
    	}
    console.log(result);
});

Waterfall will process the first function in the array first, in this case getUserAvatar, which uses github library to do fetch metadata about airbnb’s github account.

callback(null, avatarUrl);

We can invote callback() anytime within this function.  If you pass something to the first parameter, it will immediate invoke the final callback signalfying an error has occured.  Otherwise, if you pass null as first parameter, and a value  (result of github call in this case), then async.waterfall will continue to execute the next function in the array, in this case the wrapAvatarInHtml function.  The first parameter of wrapAvatarInHtml will be the result that was passed to the callback() function from the previous function.

Was that confusing? Just think about how a water fall works, a drop of water first drips on a rock, then it splashes and drips to the next rock and so on until it hits the river beneath.

Now the second paramter of async.waterfall is the final callback.

 function(err, result) {
    	if (err) {
        		console.error(err);
        		return;
    	}
    console.log(result);
}

This is only invoked if any of the functions inside the array of tasks produces error (by passing an error object to the first parameter to callback() ) or when everything successfully executed.  err will contain a value if there was an error, and result will be the value of the variable you choose to return after going through all the functions in the stack.

let’s run it:

node index.js

If you did everything right, you should get the following output:

<img src="https://avatars.githubusercontent.com/u/698437?v=3"/>

Sweet! Let’s now talk about async.series

async.series

series(tasks, [callback])

Run the functions in the tasks array in series, each one running once the previous function has completed. If any functions in the series pass an error to its callback, no more functions are run, andcallback is immediately called with the value of the error. Otherwise, callback receives an array of results when tasks have completed.

Note that while many implementations preserve the order of object properties, the ECMAScript Language Specification explicitly states that

The mechanics and order of enumerating the properties is not specified.

So if you rely on the order in which your series of functions are executed, and want this to work on all platforms, consider using an array.

Arguments

  • tasks – An array or object containing functions to run, each function is passed a callback(err, result) it must call on completion with an error err (which can be null) and an optionalresult value.
  • callback(err, results) – An optional callback to run once all the functions have completed. This function gets a results array (or object) containing all the result arguments passed to the taskcallbacks.
var async = require('async');
var GithubApi = require('github');

var github = new GithubApi({
    	version: '3.0.0'
});<br><br>

async.series([
	    function functionOne(callback) {
        		callback(null, 'RESULT OF FUNCTION ONE');
    	},
    	function functionTwo(callback) {
        		callback(null, 'RESULT OF FUNCTION TWO');
    	},
    	function functionThree(callback) {
        		callback(null, 'RESULT OF FUNCTION Three');
	    }
], function(err, result) {
	    console.log(result);
})

async.series will execute a list of functions in order.  In the above example, functionOne will be called first, then functionTwo and lastly functionThree.  The callback() within each function is invoked each time.  If null is passed to the first parameter, it means there was no errors when executing the particular function.

], function(err, result) {
	    console.log(result);
})

The second parameter is the value you want to pass along so it can be caught at the second parameter of async.series, which is the final callback function.  It will contain an array or object of all the values from each function.

if you run it:

node index.js

You should see:

[
    RESULT OF FUNCTION ONE,
    RESULT OF FUNCTION TWO,
    RESULT OF FUNCTION THREE
]

cool isn’t it?

That is it for our quick intro to some of the most useful functions from async.js Visit the Youtube playlist for a list of other not so common but useful functions:

Comments Or Questions? Discuss In Our Discord

If you enjoyed this tutorial, make sure to subscribe to our Youtube Channel and follow us on Twitter @pentacodevids for latest updates!

More from PentaCode