EventEmitter в Node.js
Все асинхронные операции ввода-вывода в Node.js отправляют событие в очередь событий при завершении.
Многие объекты в Node.js генерируют события: объект net.Server генерирует событие каждый раз, когда устанавливается новое соединение, а объект fs.readStream генерирует событие при открытии файла. Все эти объекты, генерирующие события, являются экземплярами events.EventEmitter.
EventEmitter — это основной модуль в Node.js, используемый для создания, регистрации и инициирования событий.
EventEmitter — основа событийно-ориентированного программирования, помогающая разработчикам легко реализовывать механизмы публикации и подписки на события.
Класс EventEmitter
Модуль events предоставляет только один объект: events.EventEmitter.
Ядро EventEmitter — это инкапсуляция функциональности инициирования событий и прослушивания событий.
Вы можете использоватьrequire("events");для доступа к этому модулю.
Пример
var events = require('events');
// Создать объект eventEmitter
var eventEmitter = new events.EventEmitter();
Если при создании объекта EventEmitter возникает ошибка, генерируется событие error. Событие newListener генерируется при добавлении нового слушателя, а событие removeListener — при удалении слушателя.
Ниже мы используем простой пример, чтобы проиллюстрировать использование EventEmitter:
Пример
var EventEmitter = require('events').EventEmitter;
var event = new EventEmitter();
event.on('some_event', function() {
console.log('some_event event triggered');
});
setTimeout(function() {
event.emit('some_event');
}, 1000);
После запуска этого кода консоль выводит следующее через 1 секунду:some_event event triggeredПринцип состоит в том, что объект event регистрирует слушателя для события some_event. Затем через 1000 миллисекунд мы отправляем событие some_event объекту event с помощью setTimeout, после чего вызывается слушатель some_event.
Результат выполнения выглядит следующим образом:
$ node event.js some_event 事件触发
Каждое событие EventEmitter состоит из имени события и нескольких параметров. Имя события — это строка, которая обычно выражает определённый смысл. Для каждого события EventEmitter поддерживает несколько слушателей событий.
Когда событие запускается, слушатели событий, зарегистрированные для этого события, вызываются по очереди, а параметры события передаются как аргументы функции обратного вызова.
Объясним этот процесс на следующем примере:
Пример
var events = require('events');
var emitter = new events.EventEmitter();
emitter.on('someEvent', function(arg1, arg2) {
console.log('listener1', arg1, arg2);
});
emitter.on('someEvent', function(arg1, arg2) {
console.log('listener2', arg1, arg2);
});
emitter.emit('someEvent', 'arg1 parameter', 'arg2 parameter');
Выполнение приведённого выше кода даёт следующий результат:
$ node event.js listener1 arg1 参数 arg2 参数 listener2 arg1 参数 arg2 参数
В приведенном выше примере emitter регистрирует прослушиватели событий для события someEvent, а затем инициирует событие someEvent.
По результату выполнения видно, что две функции обратного вызова прослушивателей событий вызываются одна за другой. Это простейший вариант использования EventEmitter.
EventEmitter предоставляет такие свойства, какonиemit,。on— функция для привязки обработчиков событий,emitи свойство для инициирования события. Далее давайте подробнее рассмотрим свойства EventEmitter.
Методы
| Нет. | Метод и описание |
|---|---|
| 1 | addListener(event, listener) Добавляет слушатель для указанного события в конец массива слушателей. |
| 2 | on(event, listener) Регистрирует слушатель для указанного события; принимает строку с именем события и функцию обратного вызова.
server.on('connection', function (stream) {
console.log('someone connected!');
});
|
| 3 | once(event, listener) Регистрирует одноразовый слушатель для указанного события: слушатель будет вызван не более одного раза и сразу удаляется после срабатывания.
server.once('connection', function (stream) {
console.log('Ah, we have our first user!');
});
|
| 4 | removeListener(event, listener) Удаляет слушатель для указанного события. Слушатель должен быть уже зарегистрирован для этого события. Он принимает два параметра: первый — имя события, второй — имя функции обратного вызова.
var callback = function(stream) {
console.log('someone connected!');
};
server.on('connection', callback);
// ...
server.removeListener('connection', callback);
|
| 5 | removeAllListeners([event]) Удаляет все слушатели для всех событий. Если указано событие, удаляет все слушатели для указанного события. |
| 6 | setMaxListeners(n) По умолчанию EventEmitters выводят предупреждение, если добавить более 10 слушателей. Функция setMaxListeners используется для изменения стандартного ограничения на количество слушателей. |
| 7 | listeners(event) Возвращает массив слушателей для указанного события. |
| 8 | emit(event, [arg1], [arg2], [...]) Выполняет всех слушателей по порядку. Если для события зарегистрированы слушатели, возвращает true; в противном случае возвращает false. |
Методы класса
| Нет. | Метод и описание |
|---|---|
| 1 | listenerCount(emitter, event) Возвращает количество слушателей для указанного события. |
events.EventEmitter.listenerCount(emitter, eventName) //已废弃,不推荐 events.emitter.listenerCount(eventName) //推荐
Зарегистрируйте слушателя события:
Пример
const myEmitter = new EventEmitter();
// Зарегистрировать слушателя события
myEmitter.on('sayHello', (name) => {
console.log(`Hello, ${name}!`);
});
// Вызвать событие
myEmitter.emit('sayHello', 'Alice');
Вывод:
Hello, Alice!
Используйте once() для регистрации одноразового события:
myEmitter.once('init', () => {
console.log('Initialization event occurred');
});
myEmitter.emit('init'); // 打印: Initialization event occurred
myEmitter.emit('init'); // 不会再触发
Параметры могут быть переданы при инициировании события для использования слушателями:
Пример
myEmitter.on('data', (value1, value2) => {
console.log(`Полученные значения: ${value1}, ${value2}`);
});
myEmitter.emit('data', 'Node.js', 'EventEmitter');
// Вывод: Полученные значения: Node.js, EventEmitter
Если у инициированного события нет соответствующего слушателя, EventEmitter выбросит ошибку:
Пример
console.error('Событие ошибки вызвано:', err);
});
myEmitter.emit('error', new Error('Что-то пошло не так'));
// Вывод: Событие ошибки вызвано: Error: Что-то пошло не так
Events
| Нет. | Событие и описание |
|---|---|
| 1 | newListener
Это событие срабатывает, когда добавляется новый слушатель. |
| 2 | removeListener
Удаляет слушателя из указанного массива слушателей. Обратите внимание, что эта операция изменит индексы слушателей, идущих после удалённого слушателя. |
Пример
В следующем примере демонстрируется применение класса EventEmitter через событие connection.
Создайте файл main.js со следующим кодом:
Пример
var eventEmitter = new events.EventEmitter();
// Слушатель #1
var listener1 = function listener1() {
console.log('Слушатель listener1 выполнен.');
}
// Слушатель #2
var listener2 = function listener2() {
console.log('Слушатель listener2 выполнен.');
}
// Привязываем событие connection, функция-обработчик — listener1
eventEmitter.addListener('connection', listener1);
// Привязываем событие connection, функция-обработчик — listener2
eventEmitter.on('connection', listener2);
var eventListeners = eventEmitter.listenerCount('connection');
console.log(eventListeners + " слушателей прослушивают событие connection.");
// Обрабатываем событие connection
eventEmitter.emit('connection');
// Удаляем привязанную функцию listener1
eventEmitter.removeListener('connection', listener1);
console.log("listener1 больше не прослушивает.");
// Запускаем событие connection
eventEmitter.emit('connection');
eventListeners = eventEmitter.listenerCount('connection');
console.log(eventListeners + " слушателей прослушивают событие connection.");
console.log("Выполнение программы завершено.");
Результат выполнения приведённого выше кода следующий:
$ node main.js 2 个监听器监听连接事件。 监听器 listener1 执行。 监听器 listener2 执行。 listener1 不再受监听。 监听器 listener2 执行。 1 个监听器监听连接事件。 程序执行完毕。
Событие error
EventEmitter определяет специальное событие error, которое несёт семантику ошибки. Обычно мы вызываем событие error, когда происходит исключение.
Когда событие error срабатывает, EventEmitter предусматривает, что если нет соответствующего слушателя, Node.js будет рассматривать его как исключение, завершит программу и выведет сообщение об ошибке.
Обычно мы устанавливаем слушатели для объектов, которые могут вызвать событие error, чтобы предотвратить падение всей программы после возникновения ошибки. Например:
var events = require('events');
var emitter = new events.EventEmitter();
emitter.emit('error');
Во время выполнения будет отображена следующая ошибка:
node.js:201 throw e; // process.nextTick error, or 'error' event on first tick ^ Error: Uncaught, unspecified 'error' event. at EventEmitter.emit (events.js:50:15) at Object.<anonymous> (/home/byvoid/error.js:5:9) at Module._compile (module.js:441:26) at Object..js (module.js:459:10) at Module.load (module.js:348:31) at Function._load (module.js:308:12) at Array.0 (module.js:479:10) at EventEmitter._tickCallback (node.js:192:40)
Наследование EventEmitter
В большинстве случаев мы не используем EventEmitter напрямую, а наследуем его в объектах. Все основные модули, поддерживающие реакцию на события, включая fs, net и http, являются подклассами EventEmitter.
Почему мы это делаем? Есть две причины:
-
Во-первых, реализация событий в объекте с определённой конкретной функциональностью семантически уместна; прослушивание и генерация событий должны быть методами объекта.
-
Во-вторых, объектный механизм JavaScript основан на прототипах и поддерживает частичное множественное наследование. Наследование EventEmitter не нарушит исходные отношения наследования объекта.
Большинство основных модулей Node.js (таких как HTTP и файловая система) наследуют EventEmitter. Вы можете создавать свои собственные классы, наследующие EventEmitter.
Пример
const customEmitter = new MyEmitter();
customEmitter.on('customEvent', () => {
console.log('Custom event fired');
});
customEmitter.emit('customEvent');