派生的node.js EventEmitter将在命名对象属性

时间:2016-06-28 23:49:09

标签: node.js eventemitter

我一直在使用一些带有MongoDB数据库和Angular在客户端运行的node.js软件包(Express,mqtt,socket.io)创建自己的家庭自动化中心。这个项目是我第一次使用任何JavaScript,所以说我有点像菜鸟是安全的。

我一直在解决派生的EventEmitters在被引用为命名属性时调用错误的侦听器函数的问题。这是一个证明问题的最小例子:

var EventEmitter = require('events').EventEmitter;
var debug = require('debug')('test.js');
var util = require('util');

var TestEmitter = function (initialState, name) {
    var self = this;
    EventEmitter.call(self);
    this.state = initialState;
    this.name = name;
    this.setState = function (newState) {
        self.emit('change', newState, this.state);
        self.state = newState;
    };
};
util.inherits(TestEmitter, EventEmitter);

var myObj = {
    ary: [new TestEmitter(false, 'ary1'), new TestEmitter(true, 'ary2')],
    named: {
        name1: new TestEmitter(3, 'name1'),
        name2: new TestEmitter(4, 'name2')
    }
};

myObj.ary.forEach(function (aryEmitter) {
    aryEmitter.on('change', function (newState) {
        debug(aryEmitter.name + ' changed to: ' + newState);
    });
});
for (var prop in myObj.named) {
    var currEmitter = myObj.named[prop];
    debug('prop = ' + prop);
    debug('name = ' + currEmitter.name);
    currEmitter.on('change', function (newState) {
        debug(currEmitter.name + ' changed to: ' + newState);
    });
}

myObj.ary[0].setState(true);
myObj.ary[1].setState(false);
myObj.named.name1.setState(4);
myObj.named.name2.setState(5);

基本上,TestEmitter是从EventEmitter派生的对象,它会在调用change方法时广播setState事件。

myObj是对四个TestEmitter实例的引用 - 数组中有两个,命名属性中有两个。在创建myObj之后,我为每个change事件注册了一个监听器,它只是将调试输出写入控制台,其中包含调用其回调的TestEmitter实例的名称。

但是,指定的TestEmitter引用并不像我期望的那样工作。对myObj.named.name1.setState(4)myObj.named.name2.setState(5)的调用都将执行我为EventEmitter myObj.named.name2注册的回调函数。运行上面的所有内容将产生以下输出:

test.js prop = name1 +0ms
test.js name = name1 +5ms
test.js prop = name2 +0ms
test.js name = name2 +0ms
test.js ary1 changed to: true +0ms
test.js ary2 changed to: false +0ms
test.js name2 changed to: 4 +0ms
test.js name2 changed to: 5 +0ms

任何人都可以提供任何帮助吗?我已经阅读了相当多的关于创建派生EventEmitters的最佳方法,看起来我采取了正确的方法,所以我有点难过。

感谢阅读和您能提供的任何帮助!

1 个答案:

答案 0 :(得分:0)

你的问题出现在代码的这一部分:

for (var prop in myObj.named) {
    var currEmitter = myObj.named[prop];
    debug('prop = ' + prop);
    debug('name = ' + currEmitter.name);
    currEmitter.on('change', function (newState) {
        debug(currEmitter.name + ' changed to: ' + newState);
    });
}

因此,当事件被触发时,在函数处理程序内,currEmitter具有loop的最后一个元素的值。

一个简单的解决方法是在函数中使用封装,创建新范围,并在此函数中执行操作:

for (var prop in myObj.named) {
    var currEmitter = myObj.named[prop];
    (function(currEmitter){
      currEmitter.on('change', newState=>{
        debug(currEmitter.name + ' changed to: ' + newState);
      });
    })(currEmitter);
}

或者您可以使用Object.keys()

Object.keys(myObj.named).forEach(function(key){
  var currEmitter = myObj.named[key];
  currEmitter.on('change', function(newState){
    debug(currEmitter.name + ' changed to: ' + newState);
  });
});