События
Большая часть ядра API Node.js построена вокруг идиоматичной асинхронной архитектуры, основанной на обработке событий, в которой определённые типы объектов (называемые «эмиттерами») излучают именованные события, вызывающие Function объекты («слушатели»).
Например: объект 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() позволяет передавать произвольное множество аргументов функциям-слушателям. Важно помнить, что при вызове обычной функции-слушателя ключевое слово this преднамеренно устанавливается для ссылки на экземпляр EventEmitter , к которому прикреплён слушатель.
const myEmitter = new MyEmitter();
myEmitter.on('event', function(a, b) {
console.log(a, b, this, this === myEmitter);
// Prints:
// a b MyEmitter {
// domain: null,
// _events: { event: [Function] },
// _eventsCount: 1,
// _maxListeners: undefined } true
});
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<строка> | <символ> Название события, на которое подписываются -
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<EventEmitter> Эмиттер для запроса -
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)
Псевдоним для emitter.on(eventName, listener).
emitter.emit(eventName[, ...args])
-
...args<любой>
- Возвращает: <логическое>
Синхронно вызывает каждый из зарегистрированных слушателей для события с именем eventName, в порядке их регистрации, передавая предоставленные аргументы каждому.
Возвращает true , если у события были слушатели, false в противном случае.
emitter.eventNames()
- Возвращает: <Массив>
Возвращает массив, перечисляющий события, для которых эмиттер зарегистрировал слушатели. Значения в массиве будут строками или Symbol.
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<строка> | <символ> Название события, за которым ведется прослушивание - Возвращает: <целое число>
Возвращает количество слушателей, прослушивающих событие с именем eventName.
emitter.listeners(eventName)
-
eventName<строка> | <символ> - Возвращает: <Массив функций>
Возвращает копию массива слушателей для события с именем eventName.
server.on('connection', (stream) => {
console.log('someone connected!');
});
console.log(util.inspect(server.listeners('connection')));
// Prints: [ [Function] ]
emitter.off(eventName, listener)
-
eventName<строка> | <символ> -
listener<Функция> - Возвращает: <EventEmitter>
Псевдоним для emitter.removeListener().
emitter.on(eventName, listener)
-
eventName<строка> | <символ> Название события. -
listener<Функция> Функция обратного вызова - Возвращает: <EventEmitter>
Добавляет функцию обратного вызова 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<строка> | <символ> Название события. -
listener<Функция> Функция обратного вызова - Возвращает: <EventEmitter>
Добавляет однократную функцию обратного вызова 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<строка> | <символ> Название события. -
listener<Функция> Функция обратного вызова - Возвращает: <EventEmitter>
Добавляет функцию обратного вызова listener в начало массива слушателей для события с именем eventName . Проверки на то, что функция обратного вызова listener уже была добавлена, не выполняются. Несколько вызовов с одинаковой комбинацией eventName и listener приведут к добавлению и вызову функции обратного вызова listener несколько раз.
server.prependListener('connection', (stream) => {
console.log('someone connected!');
});
Возвращает ссылку на EventEmitter, чтобы вызовы можно было объединять.
emitter.prependOnceListener(eventName, listener)
-
eventName<строка> | <символ> Название события. -
listener<Функция> Функция обратного вызова - Возвращает: <EventEmitter>
Добавляет однократную функцию обратного вызова listener для события с именем eventName в начало массива слушателей. В следующий раз, когда срабатывает eventName, этот слушатель удаляется, а затем вызывается.
server.prependOnceListener('connection', (stream) => {
console.log('Ah, we have our first user!');
});
Возвращает ссылку на EventEmitter, чтобы вызовы можно было объединять.
emitter.removeAllListeners([eventName])
-
eventName<строка> | <символ> - Возвращает: <EventEmitter>
Удаляет всех слушателей или тех, которые указаны в eventName.
Следует избегать удаления слушателей, добавленных в другом месте кода, особенно когда экземпляр EventEmitter был создан другим компонентом или модулем (например, сокетами или потоками файлов).
Возвращает ссылку на EventEmitter, чтобы вызовы можно было объединять.
emitter.removeListener(eventName, listener)
-
eventName<строка> | <символ> -
listener<Функция> - Возвращает: <EventEmitter>
Удаляет указанную функцию обратного вызова 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)
-
n<целое число> - Возвращает: <EventEmitter>
По умолчанию EventEmitter будут выводить предупреждение, если для определённого события добавлено более 10 слушателей. Это полезный по умолчанию параметр, помогающий обнаруживать утечки памяти. Очевидно, что не все события должны ограничиваться только 10 слушателями. Метод emitter.setMaxListeners() позволяет изменить предел для этого конкретного экземпляра EventEmitter. Значение можно установить в Infinity (или 0) для указания неограниченного количества слушателей.
Возвращает ссылку на EventEmitter, чтобы вызовы могли быть объединены.
emitter.rawListeners(eventName)
-
eventName<строка> | <символ> - Возвращает: <Функция[]>
Возвращает копию массива слушателей для события с именем eventName, включая любые обертки (например, созданные .once()).
const emitter = new EventEmitter();
emitter.once('log', () => console.log('log once'));
// Returns a new Array with a function `onceWrapper` which has a property
// `listener` which contains the original listener bound above
const listeners = emitter.rawListeners('log');
const logFnWrapper = listeners[0];
// logs "log once" to the console and does not unbind the `once` event
logFnWrapper.listener();
// logs "log once" to the console and removes the listener
logFnWrapper();
emitter.on('log', () => console.log('log persistently'));
// will return a new Array with a single function bound by `.on()` above
const newListeners = emitter.rawListeners('log');
// logs "log persistently" twice
newListeners[0]();
emitter.emit('log');
events.once(emitter, name)[src]
-
emitter<EventEmitter> -
name<строка> - Возвращает: <Promise>
Создаёт Promise, который разрешается, когда EventEmitter испускает указанное событие, или отклоняется, когда EventEmitter испускает 'error'. Promise будет разрешен с массивом всех аргументов, испущенных для данного события.
const { once, EventEmitter } = require('events');
async function run() {
const ee = new EventEmitter();
process.nextTick(() => {
ee.emit('myevent', 42);
});
const [value] = await once(ee, 'myevent');
console.log(value);
const err = new Error('kaboom');
process.nextTick(() => {
ee.emit('error', err);
});
try {
await once(ee, 'myevent');
} catch (err) {
console.log('error happened', err);
}
}
run();
© 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-v10.x/docs/api/events.html