Расширенная, кросс-платформенная, обратносовместимая реализация модуля EventEmitter из стандартной библиотеки NodeJS
.
Дополнительное API призвано повысить удобство разработки и читаемость кода.
Для этого были внедрены следующие механизмы:
##Отличия от оригинала:
-
EventEmitter.MAX_LISTENERS = 10
Максимальное количество обработчиков события по-умолчанию. -
EventEmitter.EVENT_NEW_LISTENER = 'newListener'
Имя события, которое срабатывает при добавлении нового обработчика. -
EventEmitter.EVENT_REMOVE_LISTENER = 'removeListener'
Имя события, которое срабатывает при удалении обработчика.
В обработчик события EventEmitter.EVENT_NEW_LISTENER
передается три аргумента:
-
{String|Number} type
Тип события, на которое был добавлен обработчик. -
{Function|EventEmitter} callback
Функция или объект-обработчик события. -
{*} context
Контекст, в котором будет вызвана функция-обработчик.
Методы подписки на события имеют расширенный интерфейс, а так же новый метод EventEmitter#off
:
{EventEmitter} EventEmitter#addListener(type, listener[, context])
{EventEmitter} EventEmitter#on(type, listener[, context])
{EventEmitter} EventEmitter#once(type, listener[, context])
{EventEmitter} EventEmitter#removeListener(type, listener)
{EventEmitter} EventEmitter#off(type, listener)
Здесь:
-
{String|Number} type
Тип события. -
{Function|EventEmitter} listener
Обработчик события. В отличии от оригинальногоEventEmitter
, обработчиком события может быть другой экземплярEventEmitter
. В этом случае, вместо вызова функции, произойдет вызов одноименного события на объекте-слушателе с передачей всех аргументов оригинального события (см. делегирование событий). Что бы отвязать объект-слушатель, его нужно передать в соответствующий метод удаления обработчика. -
{Object|null} [context=this]
Необязательный аргумент, задает контекст обработчика события.
Данными события являются все параметры (кроме первого, типа события), переданные в метод EventEmitter#emit
. Любой обработчик события может динамически менять набор этих данных.
-
{EventEmitter} EventEmitter#setEventData([...*] args)
Устанавливает новые данные события. Все переданные аргументы будут доступны в последующих обработчиках текущего события. Если ни одного аргумента не было передано, данные события будут удалены. -
{Array|null} EventEmitter#getEventData()
Возвращает текущие данные события в виде массива. -
{String|Number|null} EventEmitter#getEventType()
Возвращает тип текущего события.
new EventEmitter()
.on('event', function (foo) {
foo === 'bar'; // true
this.setEventData('baz');
})
.on('event', function (foo) {
foo === 'baz'; // true
this.setEventData();
})
.on('event', function () {
arguments.length; // 0
this.getEventType(); // 'event'
})
.emit('event', 'bar');
Делегирование удобно, если необходимо вызвать событие на одном объекте, когда происходит событие на другом. В оригинальном API пришлось бы написать примерно это:
var emitter = new EventEmitter();
var listener = new EventEmitter();
listener
.on('otherEvent', function (foo) {
foo === 'bar'; // true
});
emitter
.on('event', function (foo) {
listener.emit('otherEvent', foo);
})
.emit('event', 'bar');
Для подобных ситуаций существует два метода:
-
{EventEmitter} EventEmitter#delegate(emitter, type[, alias=type])
Делегирует событиеtype
на объектemitter
. Необязательным аргументом задается имя события, которое будет вызвано на объектеemitter
. -
{EventEmitter} EventEmitter#unDelegate(emitter, type)
Снимает делегирование событияtype
на объектemitter
.
Вышепреведенный пример теперь можно записать так:
var emitter = new EventEmitter();
var listener = new EventEmitter();
listener
.on('someEvent', function (foo) {
foo === 'bar'; // true
});
emitter
.delegate(listener, 'event', 'someEvent')
.emit('event', 'bar');
Следующие строки:
emitter.delegate(listener, 'event');
emitter.delegate(listener, 'event', 'event');
эквивалентны:
emitter.on('event', listener);
, a эта строка:
emitter.unDelegate(listener, 'event');
эквивалентна:
emitter.off('event', listener);
Стандартное API предполагает безоговорочное выполнение всех обработчиков событий. Возможность остановки выполнения дает дополнительную гибкость в написании логики обработчиков событий.
{Boolean} EventEmitter#stopEmit([type])
Останавливает выполнение обработчиков события текущего объекта. В этом методе так же доступна фильтрация по типу события.
Метод возвращает true
, если выполнение обработчиков было остановлено, либо false
в противном случае.
Несколько примеров:
new EventEmitter()
.on('event', function () {
// Эта строка остановит любое событие,
// не зависимо от типа или объекта, вызвавшего данное событие.
this.stopEmit(); // true
})
.on('event', function () {
// Этот обработчик никогда не будет вызван.
})
.emit('event');
function listener () {
this.stopEmit('error'); // true, будет остановлено только событие error
new EventEmitter().stopEmit(); // false, другой экземпляр не останавливает выполнение
}
new EventEmitter()
.on('data', listener)
.on('error', listener)
.emit('data');
Не реализованы.
Тесты в NodeJS и во всех браузерах:
npm install; npm test
.
Сравнение производительности с нативным EventEmitter-ом:
cd tests/benchmark; npm test