JavaScript中PubSub /过多事件和事件处理程序的性能成本?

时间:2014-12-28 15:16:23

标签: javascript node.js performance events publish-subscribe

Pub Sub / Event Driven architecture 是客户和客户界的常见做法。服务器端JavaScript。我的任务是使用Dojo作为前端和node.js作为后端来构建一个非常大的Web应用程序。 Pub / Sub seems very appealing因为它允许团队之间的大量并行。我担心,如果会有性能影响。

我有一个关于JavaScript中事件和事件处理程序成本的一般性问题。我已经看过thisthisthis,甚至thisthis。但我似乎还没有看到一个通用的答案。独立于框架,假设我们有 2种方法

publish() //Like jQuery's / Dojo's trigger(), EventEmitter's emit()

subscribe() //Like jQuery's / Dojo's / EventEmiter's / DOM's on() connect() live() addEventListener()

问题1:每个事件触发器的成本是多少?

案例1:清洁(松散耦合)代码强调Pub / Sub

object.publish('message1', data);
object.publish('message2', data);
...
object.publish('message100', data);

//All these are in separate files / modules    
subscribe (object, 'message1', function (data) { A()...})
subscribe (object, 'message2', function (data) { B()...})
subscribe (object, 'message100', function (data) { Z()...})

案例2:紧密耦合的代码!但是它的性能更高吗?

data.message = 'message1'
object.publish('message', data)
subscribe (object, 'message', function (data) {
  switch (data) {
    case 'message1':
      A();
      break();    
    case 'message2':
      B();
      break();
     ...
    case 'message100':
      Z();
      break();
  }
})

问题2:每个事件监听器的成本是多少?

object.publish('event', data);

案例1:再次,Cleaner(松散耦合)代码强调Pub / Sub

//A.js    
subscribe (object, 'event', function (data) {
   A();
});

//B.js
subscribe (object, 'event', function (data) {
   B();
});

//C.js
subscribe (object, 'event', function (data) {
   C();
});

案例2:再次,紧密耦合的代码!但是它的性能更高吗?

subscribe (object, 'event', function (data) {
   A();
   B();
   C();
});

Q1:有人可以指出我在客户端(使用DOMEvents或自定义事件),服务器端({}进行研究和性能测试 {3}}以及Node.js中的更多信息)?它是一个简单的例子,但很容易增长到 1000次这样的电话,因为该应用非常庞大。如果没有,我如何进行基准测试以显着降低性能?也许像EventEmitter这样的东西?知道一个人的理论基础比另一个更有效吗?

Q2:如果案例1的性能更高,编写松散耦合代码的最佳方法是什么?找到中间地带的任何方法?编写代码如案例1 ,但是一些中间编译/构建过程将其转换为案例2 (类似于jsperf在其他案例中做的事情?)说使用[Esprima]。我讨厌使构建过程复杂化。性能提升(如果有的话)是否值得这一切?

问题3:最后,虽然我在这里寻找一个非常具体的JavaScript特定答案,但了解其他语言/环境的性能成本可能会有所帮助。在大多数情况下,事件是硬件触发的事件(使用中断的概念)对答案有贡献吗?

感谢所有直到本Q结束的人!非常赞赏!!!

1 个答案:

答案 0 :(得分:5)

正如您所建议的那样,每个体系结构决策都会产生性能影响:


回拨(最快)

一对一绑定,直接引用函数

315,873 ops/sec

发布子活动

一对多绑定,需要一个循环来调用多个回调,更多的回调=更差的性能

291,609 ops/sec

承诺(最慢)

使用延迟来确保链中的每个项目不会同时被调用,更多链=性能更差

253,301 ops/sec


我会为大多数情况选择回调,除非你有大量互连的模块,那么pub / sub很有用。

请记住,这些可以相互协调。我通常允许在同一模块中进行回调和发布/订阅事件,让开发人员选择他们更喜欢使用哪一个:

var module = {
    events: [],
    item: { 'name': 'Test module' },
    updateName: function(value, callback) {
        this.item.name = value;
        if (callback) {
            callback(this.item);
        }
        this.dispatchEvent('update', this.item);
    },
    addEvent: function(name, callback) {
        if (!this.events[name]) { this.events[name] = []; }
        this.events[name].push(callback);
    },
    removeEvent: function(name, callback) {
        if (this.events[name]) {
            if (callback) {
                for (var i=0; i<this.events[name].length; ++i) {
                  if (this.events[name][i] === callback) { this.events[name].splice(i, 1); return true; }
                }
            }
            else { delete this.events[name]; }
        }
    },
    dispatchEvent: function(name, data) {
        if (this.events[name]) {
            for (var i=0; i<this.events[name].length; ++i) {
                this.events[name][i]({ data: data, target: this, type: name});
            }
        }
    }
};

module.updateName('Dan'); // no callback, no event
module.updateName('Ted', function (item) { console.log(item); }); // callback, no event
module.addEvent('update', function (item) { console.log(item); }); // add an event
module.updateName('Sal'); // no callback, event callback
module.addEvent('update', function (item) { console.log('event2', item); }); // add a second event
module.updateName('Jim'); // no callback, two event callbacks