Promises vs Domains in node

I previously wrote a post about using domains as a general exception handling mechanism in node. And while I am no longer working on the project that I was working on at that time, I am always looking for elegant ways to handle exceptions in node.

In my humble opinion, there are alot of things to like about domains.

  1. They allow you to maintain the context of a request in a complex application. This means that you can return the exception to the appropriate user.

  2. They handle exceptions in both asyncronous and synchronous code giving a simpler exception handling syntax than mixing try catch and node callback styles.

They are not without their problems though as I have found in my previous article. I found some strange issues when working with domains with the mocha testing framework that made them less than pleasant to deal with.

I wondered, now that native promises have hit node v0.11.13 in the unstable branch, whether they suffer from similar issues. Can promises be used to solve the problems above?

So let's take one of the examples I used in my previous post and see how it would be rewritten using native promises.

The original asynchronous function returning a node style callback was:

function getAddressTryCatch(callback){  
	try{  
		// Insert some sychronous code here that might throw   
		var domainName = "nodejs" + ".org";  
  
		dns.lookup(domainName, 4, function(err, address){  
			if(err){  
				return callback(err);  
			}  
			try{  
				//This is some other code that might throw 
				console.log(address);  
				return callback(null, address);  
			}  
			catch(err){  
				return callback(err);  
			}  
		})  
	}  
	catch(err){  
		// Returning callback on process.nextTick 
		process.nextTick(function(){callback(err)});  
		return;  
	}  
  
}  

To convert this function to promises, we could start with something like:

function getAddressPromise(){
	return new Promise(function(resolve, reject){
		// Insert some sychronous code here that might throw   
		var domainName = "nodejs" + ".org"; 		
		dns.lookup(domainName, 4, function(err, address){ 
			if(err){ 
				reject(err);
			}
			//This is some other code that might throw 
	 		console.log(address);		
			resolve(address);
		});
	});
}

Then we could call it as follows:

getAddressPromise().then(function(address){	
	console.log(address);
}).catch(function(err){
 	console.log("Error Caught");
});

This is similar to our original function and looking more elegant but there's a problem, while it can handle exceptions near the comment // Insert some sychronous code here that might throw, it can't handle exceptions near the comment //This is some other code that might throw. Any context will be lost and it will crash the node process. While some people advocate this sort of behaviour, I beleive it is essential to pass back some sort of error to the user and as much context as possible to admins and developers to assist with debugging.

So, the trick then is to move the code that is being executed after the async callback into the promise chain as follows.

function getAddressPromise(){
	return new Promise(function(resolve, reject){
		// Insert some sychronous code here that might throw   
		var domainName = "nodejs" + ".org"; 		
		dns.lookup(domainName, 4, function(err, address){ 
			if(err){ 
				reject(err);
			}			
			resolve(address);
		});
	});
}

getAddressPromise().then(function(address){	
	//This is some other code that might throw 
    console.log(address);
}).catch(function(err){
 	console.log("Error Caught");
});

We need to be careful not to put much code in the callback function. Just the simple reject and resolve calls.

The syntax for handling long chains of asynchronous calls is really interesting too with a simple catch at the end that can handle any exception in the chain and manage the context in a straightforward way.

My initial feeling is that promises look kind of er promising. They seem to do all that domains did but in a more elegant way. The other great thing is that promises are baked into Javascript so more and more you will be able to use them both client and server side. It's still early days for me though. I am using them in some Chrome only single page apps and will have to wait till they reach the stable branch of node.

In the meantime, I will be looking at using a polyfill to enable them in production.