Theme by nostrich (modified).
I've been doing a bit of hacking on a nodejs project recently, and this problem left me scratching my head for a while.
Create a file containing just the following code, name it module.js:
exports.someMethod = function(){
someVar = 2;
setTimeout(function(){
console.log(someVar);
}, 500);
}
exports.anotherMethod = function(){
someVar = 3;
setTimeout(function(){
console.log(someVar);
}, 500);
}
Now, if you create the following program and save it as app.js:
var module = require('./module')
module.someMethod();
module.anotherMethod();
What would running node app.js give you as output? Obviously this being a 'weird JavaScript side-effects' question you can probably guess that:
2
3
Isn't the right answer. Of course, you'll get the following output:
3
3
Try switching the someMethod and anotherMethod calls in app.js, you'll now get
2
2
So what's happening? Well the issue is scope, specifically the scope of those someVar declarations. Try changing module.js to look like this:
exports.someMethod = function(){
var someVar = 2;
setTimeout(function(){
console.log(someVar);
}, 500);
}
exports.anotherMethod = function(){
var someVar = 3;
setTimeout(function(){
console.log(someVar);
}, 500);
}
Notice those little var's when we're declaring the someVar variables? This tells the V8 compiler that the someVar variable is being declared in the scope of the function it is being declared in. Previously by leaving out the var the compiler was guessing, and putting the variable into the scope of the entire module.
The problem only appears when you access the variable inside a callback; which in this case is the code inside the setTimeout callback, but it could be any callback function. Follow closely:
someMethod which sets someVar to be 2 in the modules scope.anotherMethod which sets someVar to be 3someMethod fires after 500ms, which accesses someVar and its value (which is now 3)anotherMethod fires around about the same time, which gets someVar and its value of 3.The solution is to declare someVar in a lower scope than the module itself. This effectively creates 2 someVar's - one belonging to each method, rather than the one module-level one we had before.
This is quite an important concept, we're basically touching the realm of closures, which is one of the trickiest things about JavaScript if you're coming from a more traditional (synchronous) type of programming. I hope to talk more about these later.