События
Большая часть ядра Node.js API построена вокруг идиоматической асинхронной архитектуры, управляемой событиями, в которой определенные типы объектов (называемые «эмиттерами») периодически излучают именованные события, вызывающие функции («слушатели»).
Например: объект net.Server излучает событие каждый раз, когда к нему подключается узел; объект fs.ReadStream излучает событие при открытии файла; поток stream излучает событие всякий раз, когда данные доступны для чтения.
Все объекты, излучающие события, являются экземплярами класса EventEmitter. Эти объекты предоставляют функцию eventEmitter.on(), которая позволяет присоединить одну или несколько функций к именованным событиям, излучаемым объектом. Как правило, имена событий — это строчные camelCase, но можно использовать любой допустимый ключ свойства JavaScript.
Когда объект EventEmitter излучает событие, все функции, присоединенные к этому конкретному событию, вызываются синхронно. Любые значения, возвращаемые вызываемыми слушателями, игнорируются и будут отброшены.
Следующий пример демонстрирует простой экземпляр EventEmitter с одним слушателем. Метод eventEmitter.on() используется для регистрации слушателей, а метод eventEmitter.emit() используется для запуска события.
const EventEmitter = require('events');
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
myEmitter.on('event', () => {
console.log('an event occurred!');
});
myEmitter.emit('event');
Передача аргументов и this слушателям
Метод eventEmitter.emit() позволяет передавать произвольное множество аргументов функциям-слушателям. Важно помнить, что при вызове обычной функции-слушателя объектом EventEmitter, стандартное ключевое слово this преднамеренно устанавливается для ссылки на экземпляр EventEmitter, к которому прикреплен слушатель.
const myEmitter = new MyEmitter();
myEmitter.on('event', function(a, b) {
console.log(a, b, this);
// Prints:
// a b MyEmitter {
// domain: null,
// _events: { event: [Function] },
// _eventsCount: 1,
// _maxListeners: undefined }
});
myEmitter.emit('event', 'a', 'b');
Возможна регистрация слушателей с использованием ES6 стрелочных функций, однако в этом случае ключевое слово this больше не будет ссылаться на экземпляр EventEmitter:
const myEmitter = new MyEmitter();
myEmitter.on('event', (a, b) => {
console.log(a, b, this);
// Prints: a b {}
});
myEmitter.emit('event', 'a', 'b');
Асинхронные и синхронные
Объект EventEmitter вызывает все слушатели синхронно в порядке их регистрации. Это важно для обеспечения правильной последовательности событий и предотвращения гонок или логических ошибок. При необходимости функции-слушатели могут переключиться на асинхронный режим работы, используя методы setImmediate() или process.nextTick():
const myEmitter = new MyEmitter();
myEmitter.on('event', (a, b) => {
setImmediate(() => {
console.log('this happens asynchronously');
});
});
myEmitter.emit('event', 'a', 'b');
Обработка событий только один раз
При регистрации слушателя с помощью метода eventEmitter.on() этот слушатель будет вызываться каждый раз при излучении именованного события.
const myEmitter = new MyEmitter();
let m = 0;
myEmitter.on('event', () => {
console.log(++m);
});
myEmitter.emit('event');
// Prints: 1
myEmitter.emit('event');
// Prints: 2
Используя метод eventEmitter.once(), можно зарегистрировать слушателя, который вызывается не более одного раза для конкретного события. После излучения события слушатель снимается с регистрации, а затем вызывается.
const myEmitter = new MyEmitter();
let m = 0;
myEmitter.once('event', () => {
console.log(++m);
});
myEmitter.emit('event');
// Prints: 1
myEmitter.emit('event');
// Ignored
События ошибок
При возникновении ошибки в экземпляре EventEmitter обычно излучается событие 'error'. Они рассматриваются как специальные случаи в Node.js.
Если у экземпляра EventEmitter нет хотя бы одного слушателя, зарегистрированного для события 'error' и излучается событие 'error', ошибка выбрасывается, выводится трассировка стека, и процесс Node.js завершается.
const myEmitter = new MyEmitter();
myEmitter.emit('error', new Error('whoops!'));
// Throws and crashes Node.js
Чтобы избежать аварийного завершения процесса Node.js, можно зарегистрировать слушатель на событии process объекта uncaughtException или использовать модуль domain. (Обратите внимание, что модуль domain устарел)
const myEmitter = new MyEmitter();
process.on('uncaughtException', (err) => {
console.error('whoops! there was an error');
});
myEmitter.emit('error', new Error('whoops!'));
// Prints: whoops! there was an error
В качестве лучшей практики слушатели всегда должны добавляться для событий 'error'.
const myEmitter = new MyEmitter();
myEmitter.on('error', (err) => {
console.error('whoops! there was an error');
});
myEmitter.emit('error', new Error('whoops!'));
// Prints: whoops! there was an error
Класс: EventEmitter
Класс EventEmitter определен и экспортирован модулем events:
const EventEmitter = require('events');
Все EventEmitter излучают событие 'newListener' при добавлении новых слушателей и событие 'removeListener' при удалении существующих слушателей.
Событие: 'newListener'
-
eventName<строка> | <символ> Имя события, за которым следят -
listener<Функция> Функция обработчика события
Экземпляр EventEmitter излучит собственное событие 'newListener' перед добавлением слушателя в свой внутренний массив слушателей.
Слушатели, зарегистрированные для события 'newListener' будут переданы имя события и ссылку на добавляемого слушателя.
Тот факт, что событие запускается до добавления слушателя, имеет тонкий, но важный побочный эффект: любые дополнительные слушатели, зарегистрированные для того же name внутри обратного вызова 'newListener' будут вставлены перед слушателем, который в данный момент добавляется.
const myEmitter = new MyEmitter();
// Only do this once so we don't loop forever
myEmitter.once('newListener', (event, listener) => {
if (event === 'event') {
// Insert a new listener in front
myEmitter.on('event', () => {
console.log('B');
});
}
});
myEmitter.on('event', () => {
console.log('A');
});
myEmitter.emit('event');
// Prints:
// B
// A
Событие: 'removeListener'
Событие 'removeListener' излучается после удаления listener.
EventEmitter.listenerCount(emitter, eventName)
emitter.listenerCount() вместо этого.Метод класса, возвращающий количество слушателей для данного eventName , зарегистрированных на заданном emitter.
const myEmitter = new MyEmitter();
myEmitter.on('event', () => {});
myEmitter.on('event', () => {});
console.log(EventEmitter.listenerCount(myEmitter, 'event'));
// Prints: 2
EventEmitter.defaultMaxListeners
По умолчанию, для любого отдельного события может быть зарегистрировано максимум 10 слушателей. Это ограничение можно изменить для отдельных экземпляров EventEmitter с помощью метода emitter.setMaxListeners(n). Чтобы изменить значение по умолчанию для всех экземпляров EventEmitter , можно использовать свойство EventEmitter.defaultMaxListeners.
Будьте осторожны при установке значения EventEmitter.defaultMaxListeners, так как это изменение влияет на все экземпляры EventEmitter , включая те, которые были созданы до внесения изменений. Однако вызов emitter.setMaxListeners(n) по-прежнему имеет приоритет над EventEmitter.defaultMaxListeners.
Обратите внимание, что это не жёсткое ограничение. Экземпляр EventEmitter позволит добавить больше слушателей, но выведет предупреждение в stderr, указывающее на обнаружение возможной утечки памяти EventEmitter. Для любого отдельного EventEmitter, методы emitter.getMaxListeners() и emitter.setMaxListeners() можно использовать для временного предотвращения этого предупреждения:
emitter.setMaxListeners(emitter.getMaxListeners() + 1);
emitter.once('event', () => {
// do stuff
emitter.setMaxListeners(Math.max(emitter.getMaxListeners() - 1, 0));
});
Флаг командной строки --trace-warnings можно использовать для отображения трассировки стека для таких предупреждений.
Выпущенное предупреждение можно просмотреть с помощью process.on('warning') и оно будет иметь дополнительные свойства emitter, type и count, соответственно, относящиеся к экземпляру эмиттера, имени события и количеству прикрепленных слушателей.
emitter.addListener(eventName, listener)
Псевдоним для emitter.on(eventName, listener).
emitter.emit(eventName[, ...args])
Синхронно вызывает каждый из слушателей, зарегистрированных для события с именем eventName, в порядке их регистрации, передавая предоставленные аргументы каждому.
Возвращает true , если событие имело слушателей, false в противном случае.
emitter.eventNames()
Возвращает массив, перечисляющий события, для которых эмиттер зарегистрировал слушателей. Значения в массиве будут строками или символами.
const EventEmitter = require('events');
const myEE = new EventEmitter();
myEE.on('foo', () => {});
myEE.on('bar', () => {});
const sym = Symbol('symbol');
myEE.on(sym, () => {});
console.log(myEE.eventNames());
// Prints: [ 'foo', 'bar', Symbol(symbol) ]
emitter.getMaxListeners()
Возвращает текущее максимальное значение слушателей для EventEmitter , которое установлено с помощью emitter.setMaxListeners(n) или по умолчанию соответствует значению EventEmitter.defaultMaxListeners.
emitter.listenerCount(eventName)
Возвращает количество слушателей, слушающих событие с именем eventName.
emitter.listeners(eventName)
Возвращает копию массива слушателей для события с именем eventName.
server.on('connection', (stream) => {
console.log('someone connected!');
});
console.log(util.inspect(server.listeners('connection')));
// Prints: [ [Function] ]
emitter.on(eventName, listener)
Добавляет функцию listener в конец массива обработчиков для события с именем eventName. Проверка на то, что listener уже добавлена, не производится. Несколько вызовов с одинаковой комбинацией eventName и listener приведут к тому, что listener будет добавлена и вызвана несколько раз.
server.on('connection', (stream) => {
console.log('someone connected!');
});
Возвращает ссылку на EventEmitter, чтобы можно было объединять вызовы.
По умолчанию обработчики событий вызываются в порядке их добавления. Метод emitter.prependListener() может быть использован в качестве альтернативы, чтобы добавить обработчик события в начало массива обработчиков.
const myEE = new EventEmitter();
myEE.on('foo', () => console.log('a'));
myEE.prependListener('foo', () => console.log('b'));
myEE.emit('foo');
// Prints:
// b
// a
emitter.once(eventName, listener)
Добавляет однократную listener функцию для события с именем eventName. В следующий раз, когда eventName будет сгенерировано, этот обработчик будет удалён, а затем вызван.
server.once('connection', (stream) => {
console.log('Ah, we have our first user!');
});
Возвращает ссылку на EventEmitter, чтобы можно было объединять вызовы.
По умолчанию обработчики событий вызываются в порядке их добавления. Метод emitter.prependOnceListener() может быть использован в качестве альтернативы, чтобы добавить обработчик события в начало массива обработчиков.
const myEE = new EventEmitter();
myEE.once('foo', () => console.log('a'));
myEE.prependOnceListener('foo', () => console.log('b'));
myEE.emit('foo');
// Prints:
// b
// a
emitter.prependListener(eventName, listener)
Добавляет функцию listener в начало массива обработчиков для события с именем eventName. Проверка на то, что listener уже добавлена, не производится. Несколько вызовов с одинаковой комбинацией eventName и listener приведут к тому, что listener будет добавлена и вызвана несколько раз.
server.prependListener('connection', (stream) => {
console.log('someone connected!');
});
Возвращает ссылку на EventEmitter, чтобы можно было объединять вызовы.
emitter.prependOnceListener(eventName, listener)
Добавляет однократную listener функцию для события с именем eventName в начало массива обработчиков. В следующий раз, когда eventName будет сгенерировано, этот обработчик будет удалён, а затем вызван.
server.prependOnceListener('connection', (stream) => {
console.log('Ah, we have our first user!');
});
Возвращает ссылку на EventEmitter, чтобы можно было объединять вызовы.
emitter.removeAllListeners([eventName])
Удаляет все обработчики или те, которые соответствуют указанному eventName.
Обратите внимание, что не рекомендуется удалять обработчики, добавленные в другом месте кода, особенно если экземпляр EventEmitter был создан каким-либо другим компонентом или модулем (например, сокетами или потоками файлов).
Возвращает ссылку на EventEmitter, чтобы можно было объединять вызовы.
emitter.removeListener(eventName, listener)
Удаляет указанный listener из массива обработчиков для события с именем eventName.
const callback = (stream) => {
console.log('someone connected!');
};
server.on('connection', callback);
// ...
server.removeListener('connection', callback);
removeListener удалит, максимум, один экземпляр обработчика из массива обработчиков. Если какой-либо обработчик был добавлен несколько раз в массив обработчиков для указанного eventName, то removeListener необходимо вызвать несколько раз, чтобы удалить каждый экземпляр.
Обратите внимание, что после того, как событие было сгенерировано, все обработчики, прикреплённые к нему на момент генерации, будут вызваны в порядке их добавления. Это подразумевает, что любые removeListener() или removeAllListeners() вызовы после генерации события и до завершения работы последнего обработчика не удалят их из emit() в процессе. Последующие события будут вести себя ожидаемо.
const myEmitter = new MyEmitter();
const callbackA = () => {
console.log('A');
myEmitter.removeListener('event', callbackB);
};
const callbackB = () => {
console.log('B');
};
myEmitter.on('event', callbackA);
myEmitter.on('event', callbackB);
// callbackA removes listener callbackB but it will still be called.
// Internal listener array at time of emit [callbackA, callbackB]
myEmitter.emit('event');
// Prints:
// A
// B
// callbackB is now removed.
// Internal listener array [callbackA]
myEmitter.emit('event');
// Prints:
// A
Поскольку обработчики управляются с помощью внутреннего массива, вызов этого изменит индексы позиций любого обработчика, зарегистрированного после удалённого обработчика. Это не повлияет на порядок вызова обработчиков, но это означает, что любые копии массива обработчиков, возвращаемые методом emitter.listeners(), необходимо будет пересоздать.
Возвращает ссылку на EventEmitter, чтобы можно было объединять вызовы.
emitter.setMaxListeners(n)
По умолчанию EventEmitters отображают предупреждение, если для определённого события добавлено более 10 обработчиков. Это полезный по умолчанию параметр, который помогает находить утечки памяти. Очевидно, что не все события должны быть ограничены всего 10 обработчиками. Метод emitter.setMaxListeners() позволяет изменить ограничение для данного экземпляра EventEmitter. Значение может быть установлено на Infinity (или 0) для указания неограниченного числа обработчиков.
Возвращает ссылку на EventEmitter, чтобы можно было объединять вызовы.
© Joyent, Inc. and other Node contributors
Licensed under the MIT License.
Node.js is a trademark of Joyent, Inc. and is used with its permission.
We are not endorsed by or affiliated with Joyent.
https://nodejs.org/dist/latest-v6.x/docs/api/events.html