I got a big confusion with race condition in nodejs. I've read some posts about this and finally, I just see a point. Though nodejs is naturally single-threaded, it's still vulnerable to race condition in some situations. This post is an example.
After I'm aware of this problem, I feel unsafe whenever I write something in node. I really appreciate if somebody help me out of this fog. And also, what are the common mistakes that I should avoid?
You should understand closures in nodejs.
Here a detailed reference: http://howtonode.org/why-use-closure
Here my short explanation: Nodejs is single-threaded, but you will call a lot of async functions. So it's single-threaded but you have to pay attention to some basic things, and race condition is one of these.
this example exposes to risk of race condition:
function A(string)
{
print = string;
database.find({$and: [{hair: 'brown'},{eyes: 'blue'}]}, function(err,data) {
console.log('after async function value is:'+print) //here lo the string we passed by invoking the function})
})
console.log('value is:'+print);
}
When you start fetching the database, you are invoking an async function. So as far as you invoke the function, the parameter you passed is printed by console.log, just after the async function is invoked. When the callback is solved (after some time..), the function inside the callback has still available all the data you passed to the function by calling A(param), because each time you call a function you create a new 'closure' and all your variables will be available till the last callback is invoked (so you can put callbacks inside callbacks inside callback and stay safe).
But you have to declare var you pass to the function in a safe way (as local var), or you will be just declaring them as implicit variable in the global context, and the value will be overwrite at each call.
so, if you are calling function A('hello') a first time, you are expecting that the callback invoked after some time will print: 'after async function value is: hello', just as when you invoked the function was printed 'value is hello' in your console. And that's right, just if you don't call the function A('fuck') before the callback of the first invocation is called. Because in this case, the second call will overwrite the value of print and when the first's invocation callback will be invoked it will find the new value of the print variable. So it will print 'after async function value is: fuck'
and so your log will sound like so, invoking this function twice in a short period of time:
'value is: hello'
'value is: fuck'
'after async function value is: fuck'
'after async function value is: fuck'
So you just have to declare your variables in a safe way, by adding var before the declaration (so they will be local variable):
function A(string)
{
var print = string;
database.find({$and: [{hair: 'brown'},{eyes: 'blue'}]}, function(err,data) {
console.log('after async function value is:'+print) //here lo the string we passed by invoking the function})
})
console.log('value is:'+print);
}
In this way you are safe. A separate 'closure' is created for each invocation of A() and the closure stays alive as long as any code inside it (including asynchronous callbacks) has not yet completed. so you can call A() as many times as you want and they will each retain separate arguments. You just have to pay attention these variables are declared as local variables!