As we all await the first cross-platform release of yield in node 0.11, we thought this would be the best time to recall the issue of “callback hell” presented first by Derek Kastner. So lets have a look at this API call below.
#!/usr/bin/env node
var request = require(’request’); // npm install request
var apiurl = ’http://nodejs.org/api/index.json’;
var callbackfn = function(error, response, body) {
if (!error && response.statusCode == 200) {
console.log(body);
}
};
request(apiurl, callbackfn);
when executed this produces the following.
Sometimes we may want to pass callbacks into callbacks. For example, suppose that we want to write the results of the above API call to disk. Below is the messy way to do this.
#!/usr/bin/env node
var request = require('request');
var apiurl = 'http://nodejs.org/api/index.json';
var fs = require('fs');
var outfile = 'index2.json';
request(apiurl, function(error, response, body){
if(!error && response.statusCode == 200){
fs.writeFile(outfile, body,function(err){
if(err) throw err;
var timestamp = new Date();
console.log("wrote %s %s", outfile, timestamp);
});
}
});
This works, but it is bad in the following respects.
Firstly, its difficult to read
Secondly, it mixes three different functions together namely -> making the HTTP request, extracting the body from the HTTP response, and writing the body to disk.
Thirdly, its hard to debug and test in isolation (REPL inconvenience).
Fourthly, because the default output of request that is passed to the callback is the 3-tuple of (error, response, body),we’re doing
This form of asynchrony will make programs harder to reason about and it will lead to ugly nested callback issue.
One way around this is to use one of the node flow control libraries, like Caolan’s async. Another is to await the introduction of yield. 🙂