是否可以拥有"线程" Node中的局部变量?

时间:2016-02-26 11:01:04

标签: node.js

我想存储一个在调用链中所有堆栈帧(自上而下)之间共享的变量。与Java或C#中的ThreadLocal非常相似。

我找到了https://github.com/othiym23/node-continuation-local-storage但是它一直在为我的所有用例丢失上下文,似乎你必须修补你正在使用的库以使其本地存储感知,这对我们来说或多或少是不可能的。代码库。

Node中确实没有其他可用选项吗?可以使用域,堆栈跟踪或类似的东西来获取当前调用链的句柄(id)。如果可以的话,我可以编写自己的线程本地实现。

3 个答案:

答案 0 :(得分:13)

是的,有可能。 Thomas Watson在他的Instrumenting Node.js in Production

中在2016年奥斯陆NodeConf上发表了演讲

它使用Node.js tracing - AsyncWrap(最终应该成为公共Node API中公认的部分)。您可以在开源Opbeat Node agent中看到一个示例,或者甚至更好的check out the talk slides and example code

答案 1 :(得分:4)

自从我最初提出这个问题已经过去一年多了,最终看起来我们在Node.js 8中以Async Hooks的形式提供了一个有效的解决方案。

https://nodejs.org/api/async_hooks.html

API仍然是实验性的,但即便如此,看起来已经有一个Continuation-Local-Storage的分支在内部使用这个新的API。

https://www.npmjs.com/package/cls-hooked

答案 2 :(得分:0)

  

TLS用于普通的单线程程序使用全局变量但在多线程情况下不合适的地方。

由于javascript没有暴露的线程,因此全局变量是您问题的最简单答案,但使用一个是不好的做法。

您应该使用闭包:只需将所有异步调用包装到函数中并在那里定义变量。

在闭包

中创建的函数和回调
  (function() (
       var visibleToAll=0;

       functionWithCallback( params, function(err,result) {
          visibleToAll++;
          // ...
          anotherFunctionWithCallback( params, function(err,result) {
             visibleToAll++
             // ...
          });
       });

       functionReturningPromise(params).then(function(result) {
          visibleToAll++;
          // ...
       }).then(function(result) {
          visibleToAll++;
          // ...
       });
    ))();

在闭包之外创建的函数

如果您要求您的变量在请求范围内未定义的函数内可见,您可以改为创建上下文对象并将其传递给函数:

  (function c() (
       var ctx = { visibleToAll: 0 };

       functionWithCallback( params, ctx, function(err,result) {
          ctx.visibleToAll++;
          // ...
          anotherFunctionWithCallback( params, ctx, function(err,result) {
             ctx.visibleToAll++
             // ...
          });
       });

       functionReturningPromise(params,ctx).then(function(result) {
          ctx.visibleToAll++;
          // ...
       }).then(function(result) {
          ctx.visibleToAll++;
          // ...
       });
    ))();

使用上面c()内部调用的所有函数的方法可以引用同一个ctx对象,但对c()的不同调用都有自己的上下文。在典型的用例中,c()将是您的请求处理程序。

将上下文绑定到this

您可以通过this调用上下文对象,将其绑定到被调用函数中的Function.prototype.call

functionWithCallback.call(ctx, ...)

...使用Function.prototype.bind创建新的函数实例:

var boundFunctionWithCallback = functionWithCallback.bind(ctx)

...或使用承诺实用程序功能,如bluebird's .bind

Promise.bind(ctx, functionReturningPromise(data) ).then( ... )

其中任何一个都会使你的函数中的ctx成为this

this.visibleToAll ++;

...但它没有超过传递上下文的真正优势 - 你的函数仍然必须知道通过this传递的上下文,你可能会意外地污染全局对象永远不用上下文调用函数。