在节点I中,看到变量初始化模块内部的变量正在混淆[一个请求完成的更改影响另一个请求].对于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); }
根据我的假设,每次新请求到达节点时都会执行从模块'a'导出的printName函数,并且每次都会有不同的范围对象.
因此,在模块中使用全局内容不会影响它们的请求.
但我发现事实并非如此.任何人都可以解释节点如何处理特定的函数模块导出[它处理缓存模块导出对象的范围的方式]以及如何跨模块中的请求克服这个共享的全局变量?
编辑[我们为每个请求执行异步任务]:在我们的实时系统中快速请求.其中基本上查询redis并响应请求.我们看到错误的响应映射到错误的请求(回复[存储在模块中的全局变量]中,redis查找错误地映射到diff req).此外,我们还有一些默认值作为全局变量,可以根据请求参数覆盖.这也搞砸了
了解正在发生的事情的第一步是了解幕后发生的事情.从语言的角度来看,节点模块并没有什么特别之处."魔力"来自于节点如何从磁盘加载文件require
.
当您调用时require
,node可以同步从磁盘读取或返回模块的缓存导出对象.在读取文件时,它遵循一组有些复杂的规则来确定读取哪个文件,但是一旦它有一个路径:
检查是否require.cache[moduleName]
存在.如果是,请返回并停止.
code = fs.readFileSync(path)
.
code
用字符串换行(连接)(function (exports, require, module, __filename, __dirname) {
...});
eval
你的包装代码并调用匿名包装函数.
var module = { exports: {} }; eval(code)(module.exports, require, module, path, pathMinusFilename);
另存module.exports
为require.cache[moduleName]
.
下次require
使用同一模块时,节点只返回缓存的exports
对象.(这是一件非常好的事情,因为初始加载过程缓慢且同步.)
所以现在你应该能够看到:
模块中的顶级代码仅执行一次.
因为它实际上是在匿名函数中执行的:
"全局"变量实际上并不是全局变量(除非您明确指定global
或不使用变量作用域var
)
这是模块获取本地范围的方式.
在您的示例中,您为每个请求require
模块化a,但由于上面概述的模块缓存机制,您实际上在所有请求中共享相同的模块范围.每个调用在其作用域链中printName
共享相同的内容a
(即使它printName
自己在每次调用时都获得了一个新的作用域).
现在你在问题中的文字代码中,这没关系:你设置a
然后在下一行使用它.控制从不离开printName
,所以a
共享的事实是无关紧要的.我的猜测是你真正的代码看起来更像:
var a; function printName(req, res) { //get param `name` from url; a = name; getSomethingFromRedis(function(result) { res.end('Hi '+a); }); } module.exports.printName = printName;
这里有一个问题,因为控制确实离开了printName
.回调最终会触发,但另一个请求a
在此期间发生了变化.
你可能想要更像这样的东西:
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); }
这样,您就可A
以为每个请求获得一个全新且独立的范围.