Рубрики
Без рубрики

Обработка событий в Node.js с Eventemitter

В этом руководстве мы погрузимся в класс Eventemitter Node, чтобы сделать приложение для испускания событий. Мы также продлим его в пользовательский класс и охватываем важные функции.

Автор оригинала: Janith Kasun.

Вступление

В этом руководстве мы посмотрим на родной узел Eventemitter класс. Вы узнаете о событиях, что вы можете сделать с Дедимотер И как использовать события в вашем приложении.

Мы также охватим, какие другие родные модули простираются от Eventemitter Класс и некоторые примеры, чтобы понять, что происходит за кулисами.

Так что в двух словах мы охватим почти все, что вам нужно знать о Eventemitter класс.

Мы будем использовать некоторые основные функции ES6, такие как классы JavaScript и функции стрелки в этом руководстве. Это полезно, но не обязательно, если у вас есть некоторые предыдущие знания синтаксиса ES6.

Что такое событие?

В целом программное обеспечение парадигма вращается вокруг событий и их использование. Архитектура, управляемая событием относительно распространена в наши дни, и приложения, ориентированные на события, и реагируют на разные виды событий.

Мы можем сказать, что ядро Node.js частично приводят к событиям, так как многие родные модули, такие как файловая система ( FS ), и поток Модуль написан как Eventemitter сами по себе.

В программировании, ориентированном с событиями, событие является результатом одного или нескольких действий. Это может быть действием пользователя или периодический выход из датчика, например.

Вы можете просмотреть программы, ориентированные на мероприятия в качестве моделей публикации-подписки, где издатель запускает события, а подписчики слушают их и действуют соответствующим образом.

Например, давайте предположим, что у нас есть сервер изображений, в котором пользователи могут загружать изображения. В программировании, ориентированном на события, действие, такое как загрузка изображения, выделяет событие. Использовать его, также будет 1..n подписчики на это событие.

Как только событие загрузки будет запущено, абонент может реагировать на него, отправив электронное письмо администратору веб-сайта, позволяя им знать, что пользователь загрузил фотографию. Другой подписчик может собирать информацию о действии и сохранять их в базе данных.

Эти события, как правило, независимы друг от друга, хотя они также могут зависеть.

Что такое события?

Eventemitter Класс – это встроенный класс, который находится в События модуль. Согласно документации:

Большая часть Node.js Core API построена вокруг идиоматической асинхронной архитектуре, приводимой к мероприятиям, в которой определенные виды объектов (называемых «эмиттеров») выделяют названные события, которые вызывают Функция объекты («слушатели»), которые будут называться

Этот класс может в некоторой степени описать как помощник в реализации PUB/Sub Model, так как она помогает Эмиттеры событий (издатели) публиковать События (сообщения) и слушатели (подписчики) действовать на эти события – простым способом.

Создание Eventemitters.

Это сказано, давайте пойдем вперед и создадим Eventemitter Отказ Это можно сделать либо через создание экземпляра самого класса, либо путем внедрения его через пользовательский класс, а затем создание экземпляра этого класса.

Создание объекта Eventemitter

Давайте начнем с простого объекта, излучающего события. Мы создадим Eventemitter Что выделяет событие, которое содержит информацию о времени безотказной работы приложения, каждую секунду.

Во-первых, импортировать Eventemitter класс от События Модули:

const { EventEmitter } = require('events');

Тогда давайте создадим Eventemitter :

const timerEventEmitter = new EventEmitter();

Чтобы опубликовать событие с этого объекта так же просто, как:

timerEventEmitter.emit("update");

Мы указали имя события и опубликовали его как событие. Ничего не происходит, поскольку нет слушателя, чтобы реагировать на это событие, хотя. Давайте сделаем это событие повторите каждую секунду.

Используя SetInterval () Способ, таймер создан, который опубликует Обновить событие каждую секунду:

let currentTime = 0;

// This will trigger the update event each passing second
setInterval(() => {
    currentTime++;
    timerEventEmitter.emit('update', currentTime);
}, 1000);

Eventemitter Экземпляр принимает имя события и произвольный набор аргументов. В этом случае мы прошли EventName как Обновить и текущее время как время от начала приложения.

Мы запускаем излучатель через Эмит () Метод, который толкает событие с информацией, которую мы предоставили.

С нашим событием-эмитром готов, давайте подписаться на событие-слушатель к нему:

timerEventEmitter.on('update', (time) => {
    console.log('Message Received from publisher');
    console.log(`${time} seconds passed since the program started`);
});

Используя на () Способ, проходящее имя события, чтобы указать, какой из них мы хотели бы прикрепить слушателя, позволяет нам создавать слушателей. На Обновить Событие, метод запускается, который регистрирует время. Вы можете добавить тот же слушатель снова и снова, и каждый из них будет подписаться на мероприятие.

Второй аргумент на () Функция является обратным вызовом, который может принять любое количество дополнительных данных, которые были выпущены событием. Каждый слушатель может выбрать, какие данные они хотят, как только заказ сохраняется.

Запуск этого скрипта должен давать:

Message Received from publisher
1 seconds passed since the program started
Message Received from publisher
2 seconds passed since the program started
Message Received from publisher
3 seconds passed since the program started
...

Напротив, мы можем использовать один раз () Способ подписаться – если вам нужно выполнить что-то только в первый раз, когда событие триггеры:

timerEventEmitter.once('update', (time) => {
    console.log('Message Received from publisher');
    console.log(`${time} seconds passed since the program started`);
});

Запуск этого кода даст:

Message Received from publisher
1 seconds passed since the program started

Eventemitter с несколькими слушателями

Теперь давайте сделаем другой вид событий с тремя слушателями. Этот будет обратный отсчет. Один слушатель будет обновлять пользователь каждую секунду, один слушатель уведомляет пользователя, когда обратный отсчет приближается к своему концу, и последний слушатель будет вызвать после окончания обратного отсчета:

  • Обновить – Это событие будет вызвать на каждом втором
  • конец – Это событие срабатывает в конце обратного отсчета
  • конец - скоро – Это событие будет вызвать за 2 секунды до завершения обратного отсчета

Давайте создадим функцию, которая создает этот эмиттер событий и возвращает его:

const countDown = (countdownTime) => {
    const eventEmitter = new EventEmitter();

    let currentTime = 0;

    // This will trigger the update event each passing second
    const timer = setInterval(() => {
        currentTime++;
        eventEmitter.emit('update', currentTime);

        // Check if countdown has reached to the end
        if (currentTime === countdownTime) {
            clearInterval(timer);
            eventEmitter.emit('end');
        }

        // Check if countdown will end in 2 seconds
        if (currentTime === countdownTime - 2) {
            eventEmitter.emit('end-soon');
        }
    }, 1000);
    return eventEmitter;
};

В этой функции мы начали мероприятие на основе интервала, которое излучает Обновить событие в интервале секунды.

На первом Если Условие, мы проверяем, достиг отсчета до конца и остановите мероприятие на основе интервала. Если так, мы стреляем конец мероприятие.

Во втором состоянии мы проверяем, находится ли обратный отсчет в 2 секундах от окончания и публиковать конец - скоро событие, если так.

Теперь давайте добавим несколько подписчиков на это событие Emitter:

const myCountDown = countDown(5);

myCountDown.on('update', (t) => {
    console.log(`${t} seconds since the timer started`);
});

myCountDown.on('end', () => {
    console.log('Countdown is completed');
});

myCountDown.on('end-soon', () => {
    console.log('Count down will end in 2 seconds');
});

Этот код должен дать:

1 seconds has been passed since the timer started
2 seconds has been passed since the timer started
3 seconds has been passed since the timer started
Count down will end in 2 seconds
4 seconds has been passed since the timer started
5 seconds has been passed since the timer started
Countdown is completed

Расширение Eventemitter.

В этом разделе давайте сделаем излучатель события с той же функциональностью, проходяя Eventemitter класс. Сначала создайте Обратный отсчет Класс, который будет обрабатывать события:

const { EventEmitter } = require('events');

class CountDown extends EventEmitter {
    constructor(countdownTime) {
        super();
        this.countdownTime = countdownTime;
        this.currentTime = 0;
    }

    startTimer() {
        const timer = setInterval(() => {
            this.currentTime++;
            this.emit('update', this.currentTime);
    
            // Check if countdown has reached to the end
            if (this.currentTime === this.countdownTime) {
                clearInterval(timer);
                this.emit('end');
            }
    
            // Check if countdown will end in 2 seconds
            if (this.currentTime === this.countdownTime - 2) {
                this.emit('end-soon');
            }
        }, 1000);
    }
}

Как видите, мы можем использовать Это .emit () внутри класса напрямую. Кроме того, StartTimer () Функция используется, чтобы позволить нам контролировать, когда начинается обратный отсчет. В противном случае он начнется, как только объект создан.

Давайте создадим новый объект Обратный отсчет И подписаться на это:

const myCountDown = new CountDown(5);

myCountDown.on('update', (t) => {
    console.log(`${t} seconds has been passed since the timer started`);
});

myCountDown.on('end', () => {
    console.log('Countdown is completed');
});

myCountDown.on('end-soon', () => {
    console.log('Count down will be end in 2 seconds');
});

myCountDown.startTimer();

Запуск это приведет к:

1 seconds has been passed since the timer started
2 seconds has been passed since the timer started
3 seconds has been passed since the timer started
Count down will be end in 2 seconds
4 seconds has been passed since the timer started
5 seconds has been passed since the timer started
Countdown is completed

псевдоним для на () Функция это AddListener () Отказ Рассмотрим конец - скоро Слушатель событий:

myCountDown.on('end-soon', () => {
    console.log('Count down will be end in 2 seconds');
});

Мы могли бы сделать то же самое с AddListener () как это:

myCountDown.addListener('end-soon', () => {
    console.log('Count down will be end in 2 seconds');
});

Они оба работают. Они почти как синонимы. Тем не менее, большинство кодеров предпочитают использовать на () Отказ

Важные функции Eventemitter

Давайте посмотрим на некоторые из важных функций, которые мы можем использовать на Eventemitter с.

EventNames ()

Эта функция вернет все активные имена слушателей в качестве массива:

const myCountDown = new CountDown(5);

myCountDown.on('update', (t) => {
    console.log(`${t} seconds has been passed since the timer started`);
});

myCountDown.on('end', () => {
    console.log('Countdown is completed');
});

myCountDown.on('end-soon', () => {
    console.log('Count down will be end in 2 seconds');
});

console.log(myCountDown.eventNames());

Запуск этого кода приведет к:

[ 'update', 'end', 'end-soon' ]

Если бы мы должны были подписаться на другое событие, такое как mycount.on («Некоторые события», ...) Новое событие также будет добавлено в массив.

Имейте в виду, что этот метод не возвращает опубликованные события. Это возвращает список событий, которые подписаны на него.

Removelistener ()

Как предполагает имя, эта функция удаляет подписанный обработчик от Eventemitter :

const { EventEmitter } = require('events');

const emitter = new EventEmitter();

const f1 = () => {
    console.log('f1 Triggered');
}

const f2 = () => {
    console.log('f2 Triggered');
}

emitter.on('some-event', f1);
emitter.on('some-event', f2);

emitter.emit('some-event');

emitter.removeListener('some-event', f1);

emitter.emit('some-event');

После первого события триггеры, поскольку оба F1 и F2 активны – обе функции будут выполнены. После этого мы удалили F1 от Eventemitter Отказ Когда мы снова излучаем событие, только F2 будет выполняться:

f1 Triggered
f2 Triggered
f2 Triggered

псевдоним для Removelistener () это ВЫКЛ () Отказ Например, мы могли бы написать:

emitter.removeListener('some-event', f1);

В виде:

emitter.off('some-event', f1);

Они оба имеют одинаковый эффект.

RemoveealLlesteners ()

Опять же, как следует из названия – эта функция удалит все слушатели из всех событий Eventemitter :

const { EventEmitter } = require('events');

const emitter = new EventEmitter();

const f1 = () => {
    console.log('f1 Triggered');
}

const f2 = () => {
    console.log('f2 Triggered');
}

emitter.on('some-event', f1);
emitter.on('some-event', f2);

emitter.emit('some-event');

emitter.removeAllListeners();

emitter.emit('some-event');

Первый Эмит () будет огонь и F1 и F2 Так как они активны в то время. После удаления их Эмит () Функция выделяет событие, но никаких слушателей не ответит на него:

f1 Triggered
f2 Triggered

Обработка ошибок

Если вы хотите излучить ошибку с помощью вашего Eventemitter это должно быть сделано с Ошибка . название события. Это стандарт для всех Eventemitter Объекты в Node.js. Это событие Должен также сопровождаться Ошибка . объект. Например, событие ошибки можно излучать так:

myEventEmitter.emit('error', new Error('Something bad happened'));

Любые слушатели для Ошибка . Событие должно иметь обратный вызов с одним аргументом для захвата Ошибка . Объект и изящно справиться с этим. Если Eventemitter излучает Ошибка . Мероприятие, но нет слушателей подписаны на Ошибка . События, программа Node.js бросит бы Ошибка . это было выделено.

Это в конечном итоге будет остановить процесс Node.js от работы и выхода из вашей программы при отображении Stacktrace для ошибки в консоли.

Давайте предположим, в нашем Обратный отсчет класс, Время отсчета Параметр не может начать быть менее 2, потому что мы не сможем вызвать событие конец - скоро иначе.

В таком случае давайте выделяем Ошибка . мероприятие:

class CountDown extends EventEmitter {
    constructor(countdownTime) {
        super();

        if (countdownTimer < 2) {
            this.emit('error', new Error('Value of the countdownTimer cannot be less than 2'));
        }

        this.countdownTime = countdownTime;
        this.currentTime = 0;
    }

    // ...........
}

Обработка этой ошибки обрабатывается так же, как и другие события:

myCountDown.on('error', (err) => {
    console.error('There was an error:', err);
});

Это считается хорошей практикой, чтобы всегда иметь слушателя для Ошибка . События.

Родные модули с использованием Eventemitter

Многие родные модули в Node.js расширяют Eventemitter класс и, таким образом, самими событиями излучатели.

Отличный пример – класс потока. Официальная документация государства:

Потоки могут быть читабельными, записи, или оба. Все потоки – это экземпляры Eventemitter Отказ

Давайте посмотрим на какую-то классическую Поток Применение:

const fs = require('fs');
const writer = fs.createWriteStream('example.txt');

for (let i = 0; i < 100; i++) {
  writer.write(`hello, #${i}!\n`);
}

writer.on('finish', () => {
  console.log('All writes are now complete.');
});

writer.end('This is the end\n');

Однако между операцией письма и Writer.end () Позвоните, мы добавили слушатель. Поток s emit a закончил событие по завершении. Другие события, такие как Ошибка . , труба и unpipe испускаются при возникновении ошибки или поток чтения трубопровод или отсоединен из потока записи.

Другим заметным классом является Child_Process класс и его Spawn () Метод:

const { spawn } = require('child_process');
const ls = spawn('ls', ['-lh', '/usr']);

ls.stdout.on('data', (data) => {
  console.log(`stdout: ${data}`);
});

ls.stderr.on('data', (data) => {
  console.error(`stderr: ${data}`);
});

ls.on('close', (code) => {
  console.log(`child process exited with code ${code}`);
});

Когда Child_Process пишет в стандартную выходную трубу, данные событие stdout (который также расширяет событий). Когда выходной поток сталкивается с ошибкой, данные Событие отправляется из Стдерр трубка.

Наконец, после выходов процесса Закрыть событие уволено.

Заключение

Управляемая событием архитектура позволяет нам создавать системы, которые являются отделен Но Высоко сплоченный Отказ События представляют результат определенного действия, а 1..n Слушатели могут быть определены для прослушивания и реагирования на них.

В этой статье мы погружались в Eventemitter класс и его функциональность. Мы создали его и использовали его напрямую, а также расширили его поведение в пользовательский объект.

Наконец, мы охватывали некоторые заметные функции класса.

Как всегда, исходный код доступен на Github Отказ