Большая часть программного интерфейса приложения основанного на ядре Node.js строится вокруг идиоматического асинхронного события управляемого архитектурой, в которой определенные виды объектов периодически создают именнованные события, которые вызывают слушатели.
Например: объект net.Server
генерирует событие каждый раз, когда пир подключается к нему; fs.ReadStream
генерирует событие при открытии файла; stream
генерирует событие, всякий раз когда данные доступны для чтения.
Все объекты, генерирующие события, являются экземплярами класса EventEmitter
. Эти объекты выставляют функцию eventEmitter.on()
, что позволяет использовать одну или несколько функций, которые будут присоединены к именованным событиям, которые генерирует объект. Как правило, имена событий это строки основанные на протоколе CAMEL но может быть использован любой действительный ключ собственности 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 to
обработчики
Метод 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 Cтрелочные Функции в качестве слушателей, однако, при этом, ключевое слово this
больше не будет ссылаться на экземпляр класса EventEmitter:
const myEmitter = new MyEmitter(); myEmitter.on('event', (a, b) => { console.log(a, b, this); // Prints: a b {} }); myEmitter.emit('event', 'a', 'b');
EventListener
вызывает всех слушателей синхронно в том порядке, в котором они были зарегистрированы. Это важно для обеспечения надлежащей последовательности событий и во избежание состояния гонки или логических ошибок. При необходимости, функции слушателя могут переключиться на асинхронный режим работы с использованием методов 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(); var m = 0; myEmitter.on('event', () => { console.log(++m); }); myEmitter.emit('event'); // Prints: 1 myEmitter.emit('event'); // Prints: 2
С помощью метода eventEmitter.once()
, можно зарегистрировать обработчик событий, который вызывается всего один раз для конкретного события. После того, как событие генерируется, обработчик становится незарегистрированными, а затем вызывается.
const myEmitter = new MyEmitter(); var 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!'));
Для защиты от сбоев процесса Node.js, обработчик может быть зарегистрирован на событие process object's uncaughtException
или может быть использован модуль domain
. (Однако следует отметить, что модуль domain не рекомендуется)
const myEmitter = new MyEmitter(); process.on('uncaughtException', (err) => { console.log('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.log('whoops! there was an error'); }); myEmitter.emit('error', new Error('whoops!')); // Prints: whoops! there was an error