Возможно ли иметь локальные переменные "thread" в узле?
Я хотел бы сохранить переменную, которая распределяется между всеми кадрами стека (сверху вниз) в цепочке вызовов. Очень похоже на ThreadLocal на Java или С#.
Я нашел https://github.com/othiym23/node-continuation-local-storage, но он продолжает терять контекст для всех моих случаев использования, и кажется, что вам нужно исправить библиотеки, которые вы используете, чтобы сделать их локальными, что более или менее невозможно для нашей базы кода.
Действительно ли в Узел нет других опций? Могут ли домены, stacktraces или что-то в этом роде использовать для получения дескриптора (id) в текущей цепочке вызовов. Если это возможно, я могу написать собственную локальную реализацию потока.
Ответы
Ответ 1
Да, это возможно. Томас Уотсон рассказал об этом в NodeConf Oslo 2016 в своем Instrumenting Node.js в производстве
Он использует трассировку Node.js - AsyncWrap (которая в конечном итоге должна стать хорошо зарекомендовавшей себя частью общего Node API). Вы можете увидеть пример в агенте Opbeat Node с открытым исходным кодом или, что еще лучше, проверить слайды и примерный код.
Ответ 2
Теперь, когда прошло более года с тех пор, как я изначально задал этот вопрос, наконец, похоже, что у нас есть рабочее решение в виде Async Hooks в Node.js 8.
https://nodejs.org/api/async_hooks.html
API все еще экспериментальный, но даже тогда похоже, что уже есть вилка Continuation-Local-Storage, которая внутренне использует этот новый API.
https://www.npmjs.com/package/cls-hooked
Ответ 3
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 .bind
Promise.bind(ctx, functionReturningPromise(data) ).then( ... )
Любой из них сделает ctx доступным внутри вашей функции следующим this
:
this.visibleToAll ++;
... однако это не имеет реальное преимущество над проходя контекст вокруг - ваша функция по- прежнему должен быть в курсе контекста прошло через this
, и вы можете случайно загрязняет глобальный объект, если вы когда - либо вызова функции без контекста.