Производительность PubSub/чрезмерных событий и обработчиков событий в JavaScript?

Паб Sub/Архитектура, управляемая событиями - это обычная практика в мире JavaScript и сервера. Мне было поручено архитектовать очень большое веб-приложение, используя Dojo в качестве Front End и node.js в качестве бэкэнд. Pub/Sub кажется очень привлекательным, поскольку он позволяет многим parallelism среди команд. Однако я боюсь, если будут последствия для производительности.

У меня есть общий вопрос о стоимости событий и обработчиков событий в JavaScript. Я уже видел этот, этот, этот, даже и . Но я по-прежнему не вижу ответа общего назначения. Независимо от фреймворков, предположим, что у нас 2 метода

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

и

subscribe() //Like jQuery / Dojo / EventEmiter / DOM 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: Опять же, очищающий (слабосвязанный) код, подчеркивающий 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 или пользовательских событий), на стороне сервера (EventEmitter и более в Node.js)? Его упрощенный пример, но он может легко вырасти до 1000 таких вызовов, так как приложение довольно велико. Если нет, то как мне перейти на бенчмаркинг для заметной деградации производительности? Может быть, с чем-то вроде jsperf? Любая теоретическая основа для того, чтобы знать hwy, более эффективна, чем другая?

Q2: Если Case 1s более эффективен, что лучший способ написать свободно связанный код? Любой метод поиска середины? Написание кода типа Case 1, но некоторый процесс компиляции/сборки промежуточной части, чтобы превратить его в Случай 2 (что-то вроде Google Closure компилятор в других перфомансах?), используя [Esprima]. Мне не нравится усложнять процесс сборки даже больше, чем есть. Это повышение производительности (если оно есть) стоит всего этого?

Q3: Наконец, хотя я ищу конкретный конкретный ответ на JavaScript, это может помочь узнать затраты на производительность на других языках/средах. Тот факт, что в большинстве случаев события вызваны аппаратным обеспечением (с использованием концепции прерываний), вносит свой вклад в ответ?

Спасибо всем, кто сделал это до конца этого Q!!! Очень ценю!!!

Ответы

Ответ 1

Каждое решение по архитектуре влияет на производительность, как вы предположили:


Обратный вызов (самый быстрый)

одно к одному, имеет прямую ссылку на функцию

315 873 ops/sec

Паб Sub Events

привязки от одного до многих, требуется, чтобы цикл вызывал несколько обратных вызовов, больше обратных вызовов = худшая производительность

291,609 оп/сек

Promises (самый медленный)

используйте задержку, чтобы гарантировать, что каждый элемент в цепочке не будет вызван одновременно, больше цепей = худшая производительность

253,301 ops/sec


Я бы выбрал обратные вызовы для большинства случаев, если у вас нет нагрузки связанных между собой модулей, тогда pub/sub полезен.

И помните, что они могут работать в тандеме друг с другом. Обычно я допускаю обратный вызов и событие 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