Don't Be Catched by Function Expressions

When preparing the second part of the Debian VM on Windows Azure I have encountered an error in azure-cli showing up only if you use affinity groups. I will explain what is the root cause of the error and why I started to use function definitions.

First I want to reproduce the issue that make the error happen using generic code.

var something = false;
if (something) {
    setTimeout(function(){
        continuation();
    }, 100);
} else {
    continuation();
}

var continuation = function() {
    console.log('Hello');
};

The important thing is that the code will work properly if something will be true. In reality it works only because setTimeout is asynchronous and the function expression will be executed before execution of anonymous function that uses continuation as a closure.

If you will only test the common case (as it was in Azure Command Line Tools case), you may not notice the obvious error - function is used before it is declared. Moving it before the if will fix the problem, but now the readability of the code is really bad, as you have higher something that will run later on. Is there a way to have readability and no issue? YES!

Don’t Use Function Expressions

I know that some tools or people are complaining about it, but just stick with function definitions in cases like above:

var something = false;
if (something) {
    setTimeout(function(){
        continuation();
    }, 100);
} else {
    continuation();
}

function continuation() {
    console.log('Hello');
}

Why it is working now?

Hoisting in JavaScript

JavaScript is a peculiar language when it comes to variables visibility. The variables defined with var keyword no matter whey they are in the function will always look from code perspective like they defined at the beginning of the function. But this is not taking into account their values - they will be available when the code will reach the line with var. This is exactly what happened in the first example as function was assigned to variable after reaching the var line.

If you define a function in JavaScript in other function not only the name is hoisted, but the function body too. This means that when starting the first line of function code it looks from code perspective like all direct inner function are already defined and just waiting to be called. The change is small but very important if function is called once asynchronously and once synchronously.

A Bonus - Removing Some of Callback Hell

The nice thing about this technique is that it allows to remove some of the callbacks hell without using modules like async or promises.

(function(){
    someAsyncFunction(next1);

    function next1(err, data) {
        if (!err) {
            someOtherAsyncFunction(data, next2);
        }
    }

    function next2(err, data) {
        // see above
    }

})();

Comments