How to wait for 2+ asynchronous responses on NodeJS (CommonJS Promises)

Have you ever need to wait for several, unrelated asynchronous functions or processes on NodeJS in order to met dependencies of a last request/method/function/etc? I’m really sure you have, and it can be a pain if you do not know how to properly handle it. The worst and common case is having each one of this nested on then previous one.

There is a really easy and common way I’ve seen lately, that is by using Async.js which is not bad, but it is not that good either, mainly cause it is not a standard. If you want (and you should really want!) a standard way to do such tasks (the right way) then you should start using Promises.

From CommonJS:

Promises provide a well-defined interface for interacting with an object that represents the result of an action that is performed asynchronously, and may or may not be finished at any given point in time. By utilizing a standard interface, different components can return promises for asynchronous actions and consumers can utilize the promises in a predictable manner. Promises can also provide the fundamental entity to be leveraged for more syntactically convenient language-level extensions that assist with asynchronicity.

 

In this example I’ll be demotrating how to use Q (Promises/B proposal by Kris Kowal)  in 3 different use cases: standard functions, NodeJS like functions (callbacks) and finally a simple deferred implementation.

Standard Functions

Just a common JavaScript function that receives 0 or more parameters and returns a value.

// Module Dependencies
var Q = require('q');

// Some random numbers to play with, and a queue to keep the code readable
var numbers = [Math.random(), Math.random(), Math.random(), Math.random(), Math.random()],
	queue = [];

// A slow useless function to exemplify
function slowFunction(times) {
	var i = 0,
		times = parseInt(1000000000 * times);

	while (i < times) {
		i += 1;
	}

	return times;
}

// For each random number, create a function call and addit to the queue ;) 
numbers.forEach(function(number) {
	queue.push(Q.call(slowFunction, this, number));
});

// Q.all: execute an array of 'promises' and 'then' call either a resolve 
// callback (fulfilled promises) or reject callback (rejected promises)
Q.all(queue).then(function(ful) {

	// All the results from Q.all are on the argument as an array
	console.log('fulfilled', ful);
}, function(rej) {

	// The first rejected (error thrown) will be here only
	console.log('rejected', rej);
}).fail(function(err) {

	// If something whent wrong, then we catch it here, usually when there is no
	// rejected callback.
	console.log('fail', err);
}).fin(function() {

	// Finally statemen; executed no matter of the above results
	console.log('finally');
});

Output

fulfilled [ 149412936, 355777001, 942437362, 998285385, 448049797 ]
finally

 

NodeJS like functions (Inverse of Control)

Most NodeJS callbacks will return 1 or 2 params, first one being the error (if any) and the second the result.

// Module Dependencies
var Q = require('q'),
	request = require('request');

// Some URLs to play with, and a queue to keep the code readable
var urls = ['google.com', 'twitter.com', 'facebook.com'],
	queue = [];

// A standard NodeJS function: a parameter and a callback; the callback
// will return error (if any) as first parameter and result as second 
// parameter.
function fetchUrl(url, callback) {
	request({
		url: 'http://' + url
	}, function(err, res, body) {
		if (err) {
			callback(err, null);
		} else {
			// For brevity, on this example we are only interested on the headers
			callback(null, res.headers);
		}
	});
}

// For each url, create a function call and addit to the queue ;) 
urls.forEach(function(url) {
	queue.push(Q.ncall(fetchUrl, this, url));
});

// Q.all: execute an array of 'promises' and 'then' call either a resolve 
// callback (fulfilled promises) or reject callback (rejected promises)
Q.all(queue).then(function(ful) {

	// All the results from Q.all are on the argument as an array
	console.log('fulfilled', ful);
}, function(rej) {

	// The first rejected (error thrown) will be here only
	console.log('rejected', rej);
}).fail(function(err) {

	// If something whent wrong, then we catch it here, usually when there is no
	// rejected callback.
	console.log('fail', err);
}).fin(function() {

	// Finally statemen; executed no matter of the above results
	console.log('finally');
});

Output (Omitted in sake of brevity)

fulfilled [ { ... }, { ... }, { ... } ]
finally

 

Deferred

On a previous post I exemplified how to use jQuery’s When which is the same concept of Promise. This time this is the NodeJS version of a Promise.

// Module Dependencies
var Q = require('q'),
	request = require('request');

// Some URLs to play with, and a queue to keep the code readable
var urls = ['google.com', 'twitter.com', 'facebook.com'],
	queue = [];

// A standard NodeJS function: a parameter and a callback; the callback
// will return error (if any) as first parameter and result as second 
// parameter.
function fetchUrl(url) {
	// The 'deferred' object, base of the Promises proposal on CommonJS
	var deferred = Q.defer();

	request({
		url: 'http://' + url
	}, function(err, res, body) {
		if (err) {
			// An error? Reject the promise
			deferred.reject(err);
		} else {
			// Success! Resolve the promise
			// For brevity, on this example we are only interested on the headers
			deferred.resolve(res.headers);
		}
	});

	// Return only the promise which will allow the usage of 'when' and 'then'
	// among others
	return deferred.promise;
}

// For each url, create a function call and addit to the queue ;) 
urls.forEach(function(url) {
	queue.push(fetchUrl(url));
});

// Q.all: execute an array of 'promises' and 'then' call either a resolve 
// callback (fulfilled promises) or reject callback (rejected promises)
Q.all(queue).then(function(ful) {

	// All the results from Q.all are on the argument as an array
	console.log('fulfilled', ful);
}, function(rej) {

	// The first rejected (error thrown) will be here only
	console.log('rejected', rej);
}).fail(function(err) {

	// If something whent wrong, then we catch it here, usually when there is no
	// rejected callback.
	console.log('fail', err);
}).fin(function() {

	// Finally statemen; executed no matter of the above results
	console.log('finally');
});

Output (Omitted in sake of brevity)

fulfilled [ { ... }, { ... }, { ... } ]
finally

 

Hopefully with this 3 examples you now know a different, standard, clean and elegant way to solve those more than common situations in NodeJS (and also on the browser if you do not use jQuery).

Questions? Comments? :)

9 thoughts on “How to wait for 2+ asynchronous responses on NodeJS (CommonJS Promises)

  1. Pingback: How to wait for 2+ asynchronous responses on NodeJS (CommonJS Promises) | Erick Ruiz de Chavez | Modern web development | Scoop.it

  2. Bob Corsaro

    I find something like this much easier to read.

    https://gist.github.com/2978541

    // Module Dependencies
    var step = require(‘step’)
    , request = require(‘request’)
    , inspect = require(‘util’).inspect

    // Some URLs to play with, and a queue to keep the code readable
    , urls = ['google.com', 'twitter.com', 'facebook.com']

    step
    ( function() {
    var self = this

    // For each url, create a parallel step
    urls.forEach(function(url) {
    request({url: ‘http://’+url}, self.parallel()) } ) }

    , function(err) {
    var results = Array.prototype.slice.call(arguments, 1)

    // handle errors
    if (err) {
    console.log(“rejected “+err) }

    // print results
    console.log(“fulfilled “+results.map(function(r){return inspect(r.headers)}))

    // eh, we’re done
    console.log(“finally”) } )

    Reply
    1. Erick Post author

      First of all, thanks for taking your time to read my blog post.

      Every problem have several solutions, based on the experience and expertise of the developer solving it. Step is not a Promise implementation, it is just a flow control utility. The example is intended to show some of the functions available on “Q” API, including some error handling and flow recovering which all good programmers need to implement. If I was speaking about code readability I would have done something like the example below. Also, as a word of advice, nested callbacks are far from being readable.

      var Q = require('q'),
      	request = require('request'),
      	queue = [];
      
      function fetchUrl(url, callback) {
      	request({
      		url: 'http://' + url
      	}, function(err, res, body) {
      		if (err) {
      			callback(err, null);
      			return;
      		}
      		callback(null, res.headers);
      	});
      }
      
      ['google.coms', 'twitter.com', 'facebook.com'].forEach(function(url) {
      	queue.push(Q.ncall(fetchUrl, this, url));
      });
      
      Q.all(queue).then(function(ful) {
      	console.log('fulfilled', ful);
      }).fail(function(rej) {
      	console.log('rejected', rej);
      });

      In your example there is no finally statement so, to do a fair comparison I removed it from my new example. Let’s see what happens if I change the first URL to a non existent one: google.coms

      Your example’s output:

      rejected Error: socket hang up
      
      C:\Users\501677641\Node\node_modules\step\lib\step.js:39
              throw arguments[0];
                             ^
      TypeError: Cannot read property 'headers' of undefined
          at C:\Users\501677641\Node\step.js:31:23
          at Array.map (native)
          at Function.<anonymous> (C:\Users\501677641\Node\step.js:30:40)
          at next (C:\Users\501677641\Node\node_modules\step\lib\step.js:51:23)
          at Request._callback (C:\Users\501677641\Node\node_modules\step\lib\step.js:83:14)
          at Request.callback (C:\Users\501677641\Node\node_modules\request\main.js:119:22)
          at Request.<anonymous> (native)
          at Request.emit (events.js:70:17)
          at Request.<anonymous> (C:\Users\501677641\Node\node_modules\request\main.js:521:16)
          at Request.emit (events.js:67:17)

      Wait! what happened there? Isn’t supposed to handle errors?

      Mine’s:

      rejected { [Error: socket hang up] code: 'ECONNRESET' }

      Ok! there is it, implicit error handler.

      Finally, if we speak about code validity (which is far from be a synonym of code readability) I JSHinted both yours and mine:

      Yours:

      Microsoft (R) Windows Script Host Version 5.8
      Copyright (C) Microsoft Corporation. All rights reserved.
      
      File "C:\Users\501677641\Node\step.js"
      
      Line 4 character 29: Bad line breaking before ','.
          inspect = require('util').inspect
      
      Line 8 character 56: Missing semicolon.
          urls = ['google.coms', 'twitter.com', 'facebook.com']
      
      Line 11 character 20: Missing semicolon.
          var self = this
      
      Line 17 character 26: Missing semicolon.
          }, self.parallel())
      
      Line 18 character 7: Missing semicolon.
          })
      
      Line 19 character 3: Bad line breaking before ','.
          }
      
      Line 22 character 59: Missing semicolon.
          var results = Array.prototype.slice.call(arguments, 1)
      
      Line 26 character 37: Missing semicolon.
          console.log("rejected " + err)
      
      Line 31 character 32: Missing semicolon.
          return inspect(r.headers)
      
      Line 32 character 8: Missing semicolon.
          }))
      
      Line 35 character 27: Missing semicolon.
          console.log("finally")
      
      Line 36 character 5: Missing semicolon.
          })
      
      Implied globals:
          require: 2,3,4
          console: 26,30,35
      [Finished]

      Mine:

      Microsoft (R) Windows Script Host Version 5.8
      Copyright (C) Microsoft Corporation. All rights reserved.
      
      File "C:\Users\501677641\Node\q-ncall-brief.js"
      
      Implied globals:
          require: 1,2
          console: 22,24
      [Finished]
      Reply
  3. Pingback: How to wait for 2+ asynchronous responses on NodeJS (CommonJS Promises) | kernicPanel | Scoop.it

  4. Bob Corsaro

    JSHint tests for a particular coding style. I’m not interested in arguing about coding style since the topic has already been covered ad nauseam. I disagree with your comment about nested callbacks. Do you break out a named function for every forEach loop you right? Not only do I think that it makes code harder to read, but it is not the most common practice in high quality javascript code that I’ve read. Or do you mean that step itself is a function? That’s what I like about it. It lends itself to a more functional style of coding with less moving parts.

    Reply
  5. Pingback: How to wait for 2+ asynchronous responses on NodeJS (CommonJS Promises) | Erick Ruiz de Chavez - Dougal Campbell's geek ramblings

  6. serg

    Where is the guarantee that in the first listing:
    “Q.all(queue)” will be executed only after all “number promises” are pushed to the queue (lines: 20-27…)? I thought the nodejs execute as parallel as possible, so I would assume, that Q.all(queue) could start immediately after “numbers…” initiated to execute. If it is not so, are there some links to the documentation of node.js to prove the sequential behavior (on the praxis I see – it works). Why the Q.all(queue) implicitly knows that it mus wait until the queue is ready in the previous step?

    Reply
    1. Erick Post author

      Q is a promise implementation that helps you control the flow by executing callbacks when all the methods are done, when there is some progress on them, or when something fails; it’s purpose is not to start them all at once, therefor there is no guarantee it will wait for all of the promises to be ready in order to start executing them.

      Q.all aims to help you know when all your promise objects are either resolved or rejected and based on those results know if you can continue with the results of the promises or halt due to an error.

      You can get more details regarding promises at http://wiki.commonjs.org/wiki/Promises

      Reply
      1. serg

        but what if some promises are not yet pushed into the queue? Does the Q get know, that someone working on the queue and it should wait until the queue ready is?

        Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="">