I've done both extensively. Synchronous programming with C# and asynchronous with NodeJS. I can completely understand how the patterns that asynchronous programming impose can cause confusion for someone who doesn't have that background. Callback hell is real and I have been there. It sucks... There are many packages available in the npm ecosystem to help us deal programming in this manner that said. My personal favorite is a hybrid approach of q and async's lovely waterfall function. For example:
function myAwesomeFunction () {
var q = require('q');
var async = require('async');
var deferred = q.defer();
function firstThing (callback) {
//run some code then call the callback
callback();
}
function secondThing (callback) {
//run some code then call the callback
callback();
}
async.waterfall([firstThing, secondThing], function (err) {
if (err) { return deferred.reject(err); }
return deferred.resolve();
});
return deferred.promise;
}
It might look a little weird at first if one is not used to the pattern but, if you really look, it allows us to better reason about the order in which functions are being executed and control the flow of logic in a saner way. Returning a promise instead of a callback allows us to better chain the wrapper function elsewhere in our code. There are a ton of valid approaches though. This is just what I've come to after much trial and error.