More progress figuring out asynchronous programming in node.js

I had an old recursive directory creation program I wrote a while back for a node.js server I’m running, but it never seemed to work right.

Last week I went looking for something on github, and found a gist that seemed to be what I wanted, but it wasn’t. It didn’t understand absolute paths, and split on the ‘/’ which caused it to try to create the directory ”, which failed.

So I retooled my program, and did the work of figuring out why it worked when invoked with one directory, but failed when invoked on a list of directories.

It all gets back to the fact that JavaScript is passing references around, so you have to be careful to protect variables that are used to call functions.

First, here is my final version

var fs = require('fs');

// do the actual directory making.  probably should add mode as a
// parameter somewhere
function makeme (dir,cb){
    return function(){
        //console.log('makeme: ',dir);
        fs.mkdir(dir,0777,function(err){
            if(err) { throw new Error(err); }
            if(cb) cb();
            return 1;
        });
    };
};

//
// function to handle stat
//
function handleStatforPath(path, parent,next){
    return function(err,stats){
        if(err){
            //console.log('no path ' + path + ' so recurse');
            return makeDir(parent,
                           makeme(path,next));
        }else{
            // console.log('have path, recursing ends at ',path);
            next();
        }
        return 1;
    };
}

// the function that gets exported, but doesn't actually do any directory making
//
// checks if a directory parent exists using stat.  If not, recurse into parent.
function makeDir(path,next){
    // recursively make sure that directory exists.
    if(/(.*?)\/[^/]+$/.exec(path)){
        // console.log('handling: ',path);
        fs.stat(path,handleStatforPath(path,RegExp.$1,next));
    }else{
        console.log('in make parent dir, regex failed on : ',path);
        throw new Error('regex failure ' + path);
    }
    return;
}
exports.makeDir = makeDir;

My original bug was that I wasn’t breaking out the stat handler in a separate function. That isn’t a bug per se, but within the handler I was referencing the result of the RegExp call, which was getting stomped on by other calls to the routine. The broken version was as follows, with the bug noted:

function brokenMakeDir(path,next){
    // recursively make sure that directory exists.
    if(/(.*?)\/[^/]+$/.exec(path)){
        // console.log('handling: ',path);
        fs.stat(path,function(err,stats){
            if(err){
                //console.log('no path ' + path + ' so recurse');

                // 
                // THE BUG: referencing the RegExp.$1 object inside the callback
                //
                return brokenMakeDir(RegExp.$1,
                                     makeme(path,next));
            }else{
                // console.log('have path, recursing ends at ',path);
                next();
            }
            return 1;
        });
    }else{
        console.log('in make parent dir, regex failed on : ',path);
        throw new Error('regex failure ' + path);
    }
    return;
}

Instead of referencing the RegExp.$1 value inside of the callback, the corrected version shown first sends this value to the callback initialization call, which means it gets copied as a parameter named parent. I could have done the same thing with the inline callback handler, but breaking out the callback handler makes it obvious what the problem is.

About these ads

One thought on “More progress figuring out asynchronous programming in node.js

  1. Pingback: Overcoming shy programmer syndrome « Contour Line

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s