2016-02-26 87 views
13

我想存儲在調用鏈中所有堆棧幀(自上而下)之間共享的變量。很像Java或C#中的ThreadLocal。是否有可能在Node中有「線程」局部變量?

我發現https://github.com/othiym23/node-continuation-local-storage但它保留了所有我的用例的上下文,看起來你必須修補你正在使用的庫,使它成爲本地存儲感知的,這對我們的代碼庫來說或多或少是不可能的。

Node中是否真的沒有其他選項可用?可以使用域,堆棧跟蹤或類似的東西來獲取當前調用鏈的句柄(id)。如果這是可能的,我可以寫我自己的線程本地實現。

+1

在此問題中添加一個[MCVE]將是一件非常有用的事情。 JS是單線程的,你聲明的所有變量在技術上都是線程本地的(或更少)。 – Tomalak

+0

請查看線程局部變量如何在Java,C#甚至Python中工作。這是一個非常成熟的領域,與將普通變量放在堆棧上無關。 https://en.wikipedia.org/wiki/Thread-local_storage – Joppe

+1

我知道線程本地存儲是什麼。我在說你應該添加一個代碼示例來演示你的問題。 – Tomalak

回答

12

是的,這是可能的。 Thomas Watson在NodeConf奧斯陸2016上發表了他的演講Instrumenting Node.js in Production

它使用Node.js tracing - AsyncWrap(它最終應該成爲公共節點API的一部分)。你可以在開源的Opbeat Node agent或者甚至更好的check out the talk slides and example code中看到一個例子。

+0

非常好!謝謝您的幫助。 – Joppe

+0

@Joppe你是怎麼做到的? continuation-local-storage似乎現在正在使用這種技術,因爲它依賴於談話中提到的async-listener模塊,這些模塊目前使用asyncWrap工具。 – Andy

+0

@Andy我真的放棄了這一點,因爲我最初收到的所有答案都沒有回答真正的問題,而是告訴我要傳遞一個上下文對象(以新穎的巧妙方式進行編輯)。自從Jakub做出這個發佈以來,並沒有對此進行研究,但是如果continuation-local-storage將底層技術改變爲更可靠的東西,這聽起來很有希望。也許是時候再試一次了。 – Joppe

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傳遞的上下文,並且你可能會意外地污染全局對象,如果你曾經沒有上下文調用函數。

+0

請看看http://stackoverflow.com/questions/34556637/access-request-context-anywhere。他基本上試圖解決和我一樣的用例。 您能否說明如何將基於閉包的解決方案應用於此問題(即,不必將請求對象傳遞給調用鏈中的每個方法)? – Joppe

+0

這不允許'functionWithCallback'(以及任何未在IIFE中定義的其他函數)訪問'visibleToAll'。 – robertklep

+0

不,這是爲什麼實際上有類似於ES中的線程局部變量的用例的原因之一。 – Joppe

相關問題