JavaScript 中 PubSub 的性能成本 / 过多的事件和事件处理程序
Performance Cost of PubSub / Excessive Events and Event Handlers in JavaScript?
Pub Sub/Event Driven architecture是客户端和服务器端JavaScript领域的常见做法。我的任务是构建一个非常大的 Web 应用程序,使用 Dojo 作为前端,节点.js作为后端。Pub/Sub似乎非常吸引人,因为它允许团队之间的大量并行性。不过,我担心,如果会有性能后果。
我有一个关于JavaScript中事件和事件处理程序成本的一般问题。我已经看到了这个,这个,这个,甚至这个和这个。但我似乎仍然没有看到一个通用的答案。独立于框架,假设我们有 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:强调发布/订阅的清理器(松散耦合)代码
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:再次,强调发布/订阅的更干净(松散耦合)代码
//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 或自定义事件)、服务器端(Node.js 中的事件发射器等)为此所做的研究和性能测试吗?这是一个简单的例子,但由于该应用程序非常大,因此可以轻松增长到 1000 个此类调用。如果没有,我该如何对自己进行基准测试,以应对明显的性能下降?也许用像 jsperf 这样的东西?知道一个高速公路比另一个性能更高的理论基础吗?
Q2:如果案例 1 的性能更高,那么编写松散耦合代码的最佳方法是什么?有什么方法可以找到中间立场吗?像案例 1 一样编写代码,但一些中间编译/构建过程将其转换为案例 2(类似于 Google Closure 编译器在其他性能情况下所做的?)说使用 [Esprima]。我讨厌使构建过程变得比现在更复杂。性能提升(如果有的话)值得这一切吗?
Q3:最后,虽然我在这里寻找一个非常具体的JavaScript特定答案,但了解其他语言/环境中的性能成本可能会有所帮助。在大多数情况下,事件是硬件触发的(使用中断的概念)这一事实对答案有什么贡献吗?
感谢所有坚持到本问答结束的人!!非常感谢!!
正如您所建议的那样,每个体系结构决策都会对性能产生影响:
回调(最快)
一对一绑定直接引用
函数315,873 次操作/秒
发布-订阅活动
绑定过多需要一个循环来调用多个回调,更多的回调=更差的性能
291,609 次操作/秒
承诺(最慢)
使用延迟来确保链中的每个项目都不会同时被调用,更多的链=更差的性能
253,301 次操作/秒
在大多数情况下,我会选择回调,除非您有大量互连的模块,否则 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
- 我可以使用jquery和AJAX来调用cgi-bin脚本,然后添加消息事件来处理服务器发送事件吗
- JS - 无法在自定义事件的处理程序中将其指向调用方
- 如何创建自定义事件来处理所有转换结束事件
- 如何将参数传递给事件的处理程序
- JavaScript 中的事件处理程序如何由事件循环处理
- jQuery点击事件/事件冒泡问题,当尝试突出显示一个元素并同时选中一个复选框
- onclick事件无法处理javascript中的链接
- 从jQuery处理程序中获取事件默认处理程序的结果
- 在事件上填充数组,并在事件函数处理程序外部访问它
- 理解事件委托处理
- 事件监听器处理堆叠/重叠的画布元素
- 等待事件被处理以继续执行触发该事件的函数
- 暂停在执行拖放时显示窗口的拖放事件的处理;在网格之间下降
- 服务器发送事件-事件流-触发一个PHP服务器端事件
- Javascript事件未处理脚本
- Node.js:如何在错误事件时处理域模块中的请求对象
- 在JavaScript中将一系列DOMNodeInserted事件作为单个事件进行处理
- onclick事件无法处理图像
- 事件监听器处理headtrackr.js
- 在keydown事件中处理某些键时遵从默认行为