Eliminating Callback Hell in Node.js with Synchronize
Recently, I have been working on a Node.js application that utilizes MongoDB and found myself both loving and hating Node. The asynchronous nature of Node makes [callback hell][cbh] the bane of my existence. In other languages, I expect methods that return something to... return something.
MyObj o = MyClass.getMyObj();
Node (JavaScript) prefers to make us use callbacks and nest everything.
MyClass.getMyObj( function( err, obj) {
MyObj o = obj;
});
I'm sure this has its benefits but when working with a lot of database operations, these nests can get too deep to manage pretty quickly. Luckily I came across a nice module called [Synchronize][sync] that alleviates most of these pains, by allowing you to make your code behave in a synchronous fashion. I wasn't able to find a single working example of how to use this module, so I figured I would share how I'm using it.
First, you must install Synchronize
npm install synchronize
Next, make sure you require the module. I also alias the methods to save keystrokes later:
var sync = require('synchronize');
var fiber = sync.fiber;
var await = sync.await;
var defer = sync.defer;
Then it is actually pretty easy to get the hang of. You just wrap your async code in a fiber()
and you call each async method inside of an await()
, replacing the callback with defer()
.
try {
fiber(function() {
var obj1 = await( someAsyncMethod( defer() ) );
var obj2 = await( anotherAsyncMethod( obj1, defer() ) );
var result = await( lastAsyncMethod( obj2, defer() ) );
});
} catch(err) {
//TODO Handle error
}
Just so you can get a feel for the benefits of using Synchronize, here is the same sample as it would look without Synchronize.
someAsyncMethod( function( err, result ) {
if(err)
//...
anotherAsyncMethod( result, function( err, result ) {
if(err)
//...
lastAsyncMethod( result, function( err, result ) {
if(err)
//...
// do something with result
});
});
});
I'm sure that not everone will agree but I find this to be much more unpleasant to look at than the synchronous code.
Just for fun, here is the example written in CoffeeScript.
try
fiber ->
obj1 = await someAsyncMethod defer()
obj2 = await anotherAsyncMethod obj1, defer()
result = await lastAsyncMethod obj2, defer()
catch err
# handle error
I'm pretty sure I badly explained things but hopefully someone can find this useful. Also, if there are better ways of doing things to make managing callbacks more effective, be sure to let me know. I'm pretty new to Node and always like to learn better ways of doing things!
[cbh]: http://callbackhell.com/
[sync]: http://alexeypetrushin.github.io/synchronize/docs/index.html
Written by Dan Armstrong
Related protips
4 Responses
This is similar to async-await, from what I understand, isn't it?
https://github.com/yortus/asyncawait
Man, finally I found an article where this concept is explained fast and good. I am working with zombie.js to build a scraper and I need to synchronize the actions.Thank you.
I have a question about this:
Can I build my nodejs application using this structure without any unknown considerations? This application will be a "daemon" that scrapes webpages every n b-hours or n-days.
try {
fiber(function() {
console.log("begin");
while(flag)
{
// Many scraping actions to many webpages using await object
// Sleep
}
console.log("end");
});
} catch(err) {
//TODO Handle error
}