I consider myself a very experienced node.js developer.
Yet I still wonder if there is a better way to write the following code so I don't get the pyramid of doom... Now I went easy on you, I have some code that my pyramid gets as high as 20 floors, no kidding; and that's WITH using async.js !!!
The problem is really that I have many dependencies on previews variables so everything must be nested. The guy that wrote the book "Async Javascript, build more responsive Apps with less code" explains that he would put the functions at the root scope, which sure, would get rid of the pyramid, but now you would have a whole bunch of high scope variables (possibly even global, depending at the scope you declare them at) and this pollution can result in some pretty nasty bugs (this could cause var conflicts with other scripts if set at global space (sure you could use self invoking functions, more yachhh... or even worse, since we are dealing with async, variable overrides...). In fact, the beauty of closure is pretty mush out the door.
What he recommend is doing something like:
function checkPassword(username, passwordGuess, callback) {
var passwordHash;
var queryStr = 'SELECT * FROM user WHERE username = ?';
db.query(selectUser, username, queryCallback);
function queryCallback(err, result) {
if (err) throw err;
passwordHash = result['password_hash'];
hash(passwordGuess, hashCallback);
}
function hashCallback(passwordGuessHash) {
callback(passwordHash === passwordGuessHash);
}
}
again, not a clean approach IMHO.
So, if you look at my code (again, this is just a snippet, I get much bigger nests in other places) you will often see my code getting further and further apart from the left; and that's with using things like waterfall and async forEach...
here is a small example:
ms.async.eachSeries(arrWords, function (key, asyncCallback) {
pg.connect(pgconn.dbserver('galaxy'), function (err, pgClient, pgCB) {
statement = "SELECT * FROM localization_strings WHERE local_id = 10 AND string_key = '" + key[0] + "'";
pgClient.query(statement, function (err, result) {
if (pgconn.handleError(err, pgCB, pgClient)) return;
// if key doesn't exist go ahead and insert it
if (result.rows.length == 0) {
statement = "SELECT nextval('last_resource_bundle_string_id')";
pgClient.query(statement, function (err, result) {
if (pgconn.handleError(err, pgCB, pgClient)) return;
var insertIdOffset = parseInt(result.rows[0].nextval);
statement = "INSERT INTO localization_strings (resource_bundle_string_id, string_key, string_revision, string_text,modified_date,local_id, bundle_id) VALUES ";
statement += " (" + insertIdOffset + ",'" + key[0] + "'," + 0 + ",'" + englishDictionary[key[0]] + "'," + 0 + ",10,20)";
ms.log(statement);
pgClient.query(statement, function (err, result) {
if (pgconn.handleError(err, pgCB, pgClient)) return;
pgCB();
asyncCallback();
});
});
}
pgCB();
asyncCallback();
});
});
});
On my deep scripts I counted over 25 closing parenthesis, CRAZY, and all while remembering where to call my last callBack so async continues to next iteration...
Is there a solution to this problem? Or it is just the natrure of the beast?