在节点我看到变量初始化的全局模块内这些混淆了[由一个请求所作的更改影响其他]跨请求。 对于例如:
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);
}
按我的设想,printName功能模块从“一”是每次执行导出一个新的请求命中的节点,这将有不同范围的对象每次。
因此,拥有一个模块里面的东西全局不会跨请求影响到他们。
但我看到,情况并非如此。 谁能解释如何在特定的功能节点处理模块出口[方式,处理缓存模块输出的范围对象],以及如何克服跨请求一个模块内这种共享的全局变量?
编辑[我们做异步任务的每个请求]:在我们的生活系统迅速请求。 基本上查询的Redis并响应该请求。 我们看到,错误的反应映射到错误的请求(回复[存储在一个全局变量的模块]的Redis的查找错误地映射到DIFF REQ)。 而且我们也有一些默认值可以根据请求参数重写全局变量。 这也是越来越搞砸了
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:
- Check if
require.cache[moduleName]
exists. If it does, return that and STOP.
code = fs.readFileSync(path)
.
- Wrap (concatenate)
code
with the string (function (exports, require, module, __filename, __dirname) {
... });
eval
your wrapped code and invoke the anonymous wrapper function.
var module = { exports: {} };
eval(code)(module.exports, require, module, path, pathMinusFilename);
Save module.exports
as require.cache[moduleName]
.
The next time you require
the same module, node simply returns the cached exports
object. (This is a very good thing, because the initial loading process is slow and synchronous.)
So now you should be able to see:
- Top-level code in a module is only executed once.
- Since it is actually executed in an anonymous function:
- 'Global' variables aren't actually global (unless you explicitly assign to
global
or don't scope your variables with var
)
- This is how a module gets a local scope.
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 to printName
shares the same a
in its scope chain (even though printName
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 leaves printName
, so the fact that a
is shared is irrelevant. My guess is your real code looks more like:
var a;
function printName(req, res) {
//get param `name` from url;
a = name;
getSomethingFromRedis(function(result) {
res.end('Hi '+a);
});
}
module.exports.printName = printName;
Here we have a problem because control does leave printName
. The callback eventually fires, but another request changed a
in the meantime.
You probably want something more like this:
a.js
module.exports = function A() {
var a;
function printName(req, res) {
//get param `name` from url;
a = name;
res.end('Hi '+a);
}
return {
printName: printName
};
}
index.js
var A = require('a');
function requestListener(req, res) {
var a = A();
a.printName(req, res);
}
This way, you get a fresh and independent scope inside of A
for each request.
当在这个过程中,你分配到的名字这要看。
如果在分配名称调用requestListener之间,有一个异步方法,那么我们将有“竞态条件”(即两个线程改变在同一时间同一个对象),即使是Node.js的单线程。
这是因为的node.js将启动,而异步方法是在后台运行的处理新的请求。
例如看看下面的顺序:
request1 starts processing, sets name to 1
request1 calls an async function
node.js frees the process, and handles the next request in queue.
request2 starts processing, sets name to 2
request2 calls an async function
node.js frees the process, the async function for request 1 is done, so it calls the callback for this function.
request1 calls requestListener, however at this point name is already set to 2 and not 1.
处理在Node.js的异步功能是非常相似的多线程编程,你必须小心封装数据。 一般来说,你应该尽量避免使用全局对象,如果你使用他们,他们应该是:不可变的或自成一体。
全局对象不应该被用来传递函数之间的状态(这是你在做什么)。
你的问题的解决办法应该是把名字对象中的全局,建议的地方是请求对象,它被传递到请求处理pipelie所有最重要的功能里面(这是什么connect.js,express.js和所有的中间件都在做),或在一个会话中(见connect.js会议中间件),这将让你坚持来自同一用户的不同请求之间的数据。
模块被设计为运行一次和缓存模块,即,与节点的异步性质组合装置的时间的约50% res.end('Hi '+a)
之前执行a = name
(由于是已知的)。
最终它归结为JavaScript的一个简单的事实:全局变量是邪恶的。 除非它永远不会被要求重写我不会用一个全球性的。