节点JS模块内的全局(Node js globals within modules)

2019-09-02 02:13发布

在节点我看到变量初始化的全局模块内这些混淆了[由一个请求所作的更改影响其他]跨请求。 对于例如:

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)。 而且我们也有一些默认值可以根据请求参数重写全局变量。 这也是越来越搞砸了

Answer 1:

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:

  1. Check if require.cache[moduleName] exists. If it does, return that and STOP.
  2. code = fs.readFileSync(path).
  3. Wrap (concatenate) code with the string (function (exports, require, module, __filename, __dirname) { ... });
  4. eval your wrapped code and invoke the anonymous wrapper function.

    var module = { exports: {} };
    eval(code)(module.exports, require, module, path, pathMinusFilename);
    
  5. 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.



Answer 2:

当在这个过程中,你分配到的名字这要看。

如果在分配名称调用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会议中间件),这将让你坚持来自同一用户的不同请求之间的数据。



Answer 3:

模块被设计为运行一次和缓存模块,即,与节点的异步性质组合装置的时间的约50% res.end('Hi '+a)之前执行a = name (由于是已知的)。

最终它归结为JavaScript的一个简单的事实:全局变量是邪恶的。 除非它永远不会被要求重写我不会用一个全球性的。



文章来源: Node js globals within modules