In node I see variables initialized global inside modules are getting mixed up [changes done by one request affects the other] across requests. For Ex:
a.js
var a;
function printName(req, res) {
//get param `name` from url;
a = name;
res.end('Hi '+a);
}
module.exports.printName = printName;
index.js
//Assume all createServer stuffs are done and following function as a CB to createServer
function requestListener(req, res) {
var a = require('a');
a.printName(req, res);
}
As per my assumption, printName function exported from module 'a' is executed everytime a new request hits node and it will have different scope object everytime.
So, having something global inside a module wouldn't be affecting them across requests.
But I see that isn't the case. Can anyone explain how does node handle module exports of functions in specific [way it handles the scope of the cached module exports object] and how to overcome this shared global variables across requests within a module?
Edit [We do async task per request]: With rapid requests in our live system. Which basically query redis and responds the request. We see wrong response mapped to wrong request (reply [stored in a global var in the module] of a redis look up wrongly mapped to diff req). And also we have some default values as global vars which can be overridden based on request params. Which also is getting screwed up
It really depends when in the process do you assign to name.
if between assigning the name to calling requestListener, there is an async method, then you we'll have "race conditions" (I.E. two threads changing the same object at the same time) even though node.js is single-threaded.
this is because node.js will start processing a new request while the async method is running in the background.
for example look at the following sequence:
dealing with Async function in Node.js is very similar to multi-threaded programming, you must take care to encapsulate your data. in general you should try to avoid using Global object, and if you do use them, they should be either: immutable or self-contained.
Global objects shouldn't be used to pass state between functions (which is what you are doing).
The solution to your problems should be to put the name global inside an object, the suggested places are inside the request object, which is passed to all most all functions in the request processing pipelie (this is what connect.js,express.js and all the middleware are doing), or within a session (see connect.js session middleware), which would allow you to persist data between different requests from the same user.
The first step to understanding what is happening is understanding what's happening behind the scenes. From a language standpoint, there's nothing special about node modules. The 'magic' comes from how node loads files from disk when you
require
.When you call
require
, node either synchronously reads from disk or returns the module's cached exports object. When reading files, it follows a set of somewhat complex rules to determine exactly which file is read, but once it has a path:require.cache[moduleName]
exists. If it does, return that and STOP.code = fs.readFileSync(path)
.code
with the string(function (exports, require, module, __filename, __dirname) {
...});
eval
your wrapped code and invoke the anonymous wrapper function.Save
module.exports
asrequire.cache[moduleName]
.The next time you
require
the same module, node simply returns the cachedexports
object. (This is a very good thing, because the initial loading process is slow and synchronous.)So now you should be able to see:
global
or don't scope your variables withvar
)In your example, you
require
module a for each request, but you're actually sharing the same module scope across all requrests because of the module caching mechanism outlined above. Every call toprintName
shares the samea
in its scope chain (even thoughprintName
itself gets a new scope on each invocation).Now in the literal code you have in your question, this doesn't matter: you set
a
and then use it on the very next line. Control never leavesprintName
, so the fact thata
is shared is irrelevant. My guess is your real code looks more like:Here we have a problem because control does leave
printName
. The callback eventually fires, but another request changeda
in the meantime.You probably want something more like this:
a.js
index.js
This way, you get a fresh and independent scope inside of
A
for each request.Modules were designed for run once and cache the module, that, combined with node's asynchronous nature means about 50% of the time
res.end('Hi '+a)
executes beforea = name
(because a is known).Ultimately it boils down to one simple fact of JavaScript: global vars are evil. I would not use a global unless it never gets overridden by requests.