Nesting functions in nodejs / javascript

From what I've read, nesting functions in javascript causes extra declarations / destructions which can be avoided by using "static functions" or even a Singleton implementation. Also "new" does the same thing where two instances of the function or objects are also independent copies.

Is this true? If so, what can I do to have the same functionality as with nested functions and with "new". This is for a game where the server is in nodejs / javascript. I've reached about 8 levels of nested functions and am starting to worry.

Example:

DB.cityUpdateUpkeep = function( cuid )
{
/**  @type {Array} */
var buildings = null;

DB.cityGet( cuid, function( error, city )
{
    if( error )
    {
        console.log( "Couldn't get city" );
    }
    else
    {
        DB.iBuildings.find( {cuid:cuid}, function( error, cursor )
        {
            if( error )
            {
                console.log( "-error:" );
                console.log( error );
            }
            else
            {
                cursor.toArray( function( error, response )
                {
                    if( error )
                    {
                        console.log( "-error:" );
                        console.log( error );
                    }
                    else
                    {
                        console.log( "-the response:" );
                        console.log( response );
                        buildings = response;

                        var income  = city.resources.income;
                        var storage = city.resources.storage;
                        var stored  = city.resources.stored;

                        for( var buildingID in buildings )
                        {
                            var building = buildings[ buildingID ];
                            var blueprint = DB.bBuildings[ building.buid ];

                            if( blueprint.resources.income )
                            {
                                income = Utils.sumObjects( income, blueprint.resources.income );
                            }

                            if( blueprint.resources.storage )
                            {
                                storage = Utils.sumObjects( storage, blueprint.resources.storage );
                            }

                            if( blueprint.resources.stored )
                            {
                                stored = Utils.sumObjects( stored, blueprint.resources.stored );
                            }
                        }

                        console.log( "cuid: " + city._id + " income: " + income + " storage " + storage + " stored " + stored );
                    }
                });
            }
        });
    }
});
};

Take a look at Q for ways to flatten callbacks (the code would be little nicer). For your specific example, I would prefer to refactor using a number of methods:

  1. Return methods early when err (saves you from having to nest else's)
  2. Execute asynchronous calls concurrently when they're not dependent (notice DB.cityGet and DB.iBuildings.find - makes your code run faster as well)
  3. creating functions and referencing outside the nest (ex. checkComplete)

My refactor below:

  DB.cityUpdateUpkeep = function( cuid ){
    /**  @type {Array} */
    var buildings = null;
    var city = null;
    var checkComplete = function(){
        if (!city || !builings){
            return;
        }   
        var income  = city.resources.income;
        var storage = city.resources.storage;
        var stored  = city.resources.stored;

        for( var buildingID in buildings ){
            var building = buildings[ buildingID ];
            var blueprint = DB.bBuildings[ building.buid ];

            if( blueprint.resources.income ){
                income = Utils.sumObjects( income, blueprint.resources.income );
            }   

            if( blueprint.resources.storage ){
                storage = Utils.sumObjects( storage, blueprint.resources.storage );
            }   

            if( blueprint.resources.stored ){
                stored = Utils.sumObjects( stored, blueprint.resources.stored );
            }   
        }   

        console.log( "cuid: " + city._id + " income: " + income + " storage " + storage + " stored " + stored );

    }   
    DB.cityGet(cuid, function(err, response){
        if (err){
            console.log("Couldn't get city");
            return;
        }   
        city = response;
        checkComplete();
    }); 
    DB.iBuildings.find({cuid:cuid}, function(err, cursor){
        if (err){
            console.log(err);
            return;
        }   
        cursor.toArray(function(err, response){
            if (err){
                console.log(err)
                return;
            }   
            buildings = response;
            checkComplete(); 
        }); 
    }); 
});

One solution is to store the functions instead of having anonymous callbacks. It's easier to reuse them as well.

DB.cityUpdateUpkeep = function (cuid) {
    var findHandler = function () { ... };

    DB.cityGet(cuid, function (error, city) {
        ...
        DB.iBuildings.find({cuid:cuid}, findHandler);
        ...
    });
};

I would suggest that you pick a control flow library (async is the most popular one) instead of having a bunch of callbacks, you can pick waterfall

async.parallel([
    function(callback){
        setTimeout(function(){
            callback(null, 'one');
        }, 200);
    },
    function(callback){
        setTimeout(function(){
            callback(null, 'two');
        }, 100);
    }
],
// optional callback
function(err, results){
    // the results array will equal ['one','two'] even though
    // the second function had a shorter timeout.
});

(code is borrowed from the main website of async). So if you want to share a variable between those nested functions, you can define it in DB.cityUpdateUpkeep which is the top-level, and other nested functions can use it. However, be aware that if you modify the variable at any time, the function that runs later will be affected.

And, how bad are these nested functions? Nothing, it is the nodejs nature (asynchronous programming model), you have to live with it. You will have to re-arrange your code for easy maintenance, though