События
Большая часть ядра Node.js API построена вокруг идиоматичной асинхронной архитектуры, основанной на событиях, в которой определённые типы объектов (называемые «эмиттерами») периодически излучают именованные события, вызывающие функции («слушатели»).
Например: объект net.Server излучает событие каждый раз, когда к нему подключается узел; объект fs.ReadStream излучает событие при открытии файла; поток stream излучает событие всякий раз, когда доступны данные для чтения.
Все объекты, которые излучают события, являются экземплярами класса EventEmitter. Эти объекты предоставляют функцию eventEmitter.on(), которая позволяет присоединить одну или несколько функций к именованным событиям, излучаемым объектом. Обычно имена событий — это строковые значения с использованием верблюжьего регистра, но можно использовать любое допустимое ключевое слово свойства 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, можно использовать модуль domain. (Однако, модуль domain был устарел.)
В качестве наилучшей практики, слушатели всегда должны добавляться для событий '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<any> Имя события, на которое подписываются -
listener<Function> Функция обработчика события
Экземпляр 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'
-
eventName<any> Имя события -
listener<Function> Функция обработчика события
Событие '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 . Если это значение не является положительным числом, будет выброшено исключение TypeError.
Будьте осторожны при настройке 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, соответственно, ссылающиеся на экземпляр эмиттера событий, имя события и количество прикреплённых слушателей. Свойство name установлено в значение 'MaxListenersExceededWarning'.
emitter.addListener(eventName, listener)
-
eventName<any> -
listener<Function>
Псевдоним для emitter.on(eventName, listener).
emitter.emit(eventName[, ...args])
-
eventName<any> -
...args<any>
Синхронно вызывает каждого из слушателей, зарегистрированных для события с именем 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<any> Имя события, на которое подписываются
Возвращает количество слушателей, подписывающихся на событие с именем eventName.
emitter.listeners(eventName)
-
eventName<any>
Возвращает копию массива слушателей для события с именем eventName.
server.on('connection', (stream) => {
console.log('someone connected!');
});
console.log(util.inspect(server.listeners('connection')));
// Prints: [ [Function] ]
emitter.on(eventName, listener)
-
eventName<any> Название события. -
listener<Function> Функция обратного вызова
Добавляет функцию 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)
-
eventName<any> Название события. -
listener<Function> Функция обратного вызова
Добавляет однократную функцию 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)
-
eventName<any> Название события. -
listener<Function> Функция обратного вызова
Добавляет функцию listener в начало массива слушателей для события с именем eventName. Проверка на то, что функция listener уже добавлена, не выполняется. Несколько вызовов с одинаковой комбинацией eventName и listener приведут к добавлению и вызову listener несколько раз.
server.prependListener('connection', (stream) => {
console.log('someone connected!');
});
Возвращает ссылку на EventEmitter, для возможности цепочки вызовов.
emitter.prependOnceListener(eventName, listener)
-
eventName<any> Название события. -
listener<Function> Функция обратного вызова
Добавляет однократную функцию listener для события с именем eventName в начало массива слушателей. В следующий раз, когда срабатывает eventName, этот слушатель удаляется, а затем вызывается.
server.prependOnceListener('connection', (stream) => {
console.log('Ah, we have our first user!');
});
Возвращает ссылку на EventEmitter, для возможности цепочки вызовов.
emitter.removeAllListeners([eventName])
-
eventName<any>
Удаляет всех слушателей или указанные по имени eventName.
Обратите внимание, что не рекомендуется удалять слушателей, добавленных в других частях кода, особенно если экземпляр EventEmitter был создан другим компонентом или модулем (например, сокетами или потоками файлов).
Возвращает ссылку на EventEmitter, для возможности цепочки вызовов.
emitter.removeListener(eventName, listener)
-
eventName<any> -
listener<Function>
Удаляет указанную функцию 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(), нужно будет пересоздать.
Когда одна функция была добавлена как обработчик несколько раз для одного события (как в примере ниже), removeListener() удалит последний добавленный экземпляр. В примере слушатель once('ping') удаляется:
const ee = new EventEmitter();
function pong() {
console.log('pong');
}
ee.on('ping', pong);
ee.once('ping', pong);
ee.removeListener('ping', pong);
ee.emit('ping');
ee.emit('ping');
Возвращает ссылку на 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-v8.x/docs/api/events.html