How to reduce callbacks in node.js with generators.

Wouter Van Respaille
On Coding
Published in
3 min readMay 27, 2014

--

When you write javascript on the server side and you have a lot of code you can get a loop of nested callbacks very quickly.

asyncFunction1(value, function(value1){
asyncFunction2(value1, function(value2){
asyncFunction3(value2, function(value3){
asyncFunction4(value3, function(value4){
});
});
});
})

There’s a way to prevent this endless loop of callbacks by using the “yield” keyword and generators. The first thing you have to do if you want to use these features is using ECMAScript 6 (ES6). ES6 or also known as “Harmony” is the latest major release of the ECMAScript language specification. ECMAScript is the “official” name for the language commonly referred to as JavaScript.

You can use ES6 with this line of code:

node --harmony server.js

The first thing you need to know is how generators work. Generators are in short iterators that “yields” a value each time you call the next() method. You create a generator by adding a * to the function.

function* getNextNumber() {
yield 1;
yield 2;
};
var number = new getNextNumber();
console.log(number.next()); // this will yield value '1'
console.log(number.next()); // this will yield value '2'
console.log(number.next()); // this will change the 'done' property to 'true'
console.log(number.next()); // this will throw an error

There’s also a way to create infinite sequences. In the example below you can use the doubleNumber for an infinite amount of times by using ‘while’. When you give an argument with the next function this means that the yielded value will get this value. In this case we give a boolean ‘true’ when we want to reset the doubleNumber generator.

function* doubleNumber(){
var newNumber = 1;
while (1){
newNumber = newNumber * 2;
var reset = yield newNumber;
if (reset) {
newNumber = 1;
}
}
}
var number = doubleNumber();
console.log(number.next()); // this will display "2"
console.log(number.next()); // this will display "4"
console.log(number.next()); // this will display "8"
console.log(number.next()); // this will display "16"
console.log(number.next(true)); // this will reset the number
console.log(number.next()); // this will display "2"
console.log(number.next()); // this will display "4"

Now if you want to yield your own callback functions we have to add the co package. This package actually provides co-routine in node and creates a way that the asynchronous code looks like synchronous code.

What you can do now is write your code like this:

co(function *() {
try {
var value1 = yield asyncFunction1(value);
var value2 = yield asyncFunction2(value1);
var value3 = yield asyncFunction3(value2);
var value4 = yield asyncFunction3(value3);
} catch (err) {
console.log(err);
}
});

Now this is a far more structured and clean way to write your code. And this is the way you write the ‘yielded’ code:

var asyncFunction1 = function (value) {
return function (cb) {
cb(false, myNewValue);
}
}

Because you use the co package you always have to return a function. The first argument in the callback always indicates if it is an error or not. Now lets say you want to get a user out of a ‘users’ database. You can’t alter the way the get function is written without changing the source code. So you can use the co module again for this:

var user = yield users.co.get(userId);

As not all browsers support ES6 you should only use it on the server side. This will reduce the amount of nested callbacks to clean and clear code.

--

--