Шина событий (EventBus) в JavaScipt

Доброго времени суток! Сегодня мы рассмотрим с Вами, что такое шина событий и как использовать ее в JavaScript.

Шина событий обычно используется в качестве механизма связи между несколькими компонентами, т.е. EvenBus это что-то вроде центру управления событиями. Один компонент отправляет (публикует) сообщения, а другие компоненты подписываются на них, и, при получении, обрабатывают. Таким образом обеспечивается асинхронный процесс связи между компонентами, или между плагинами и ядром системы, если в системе реализована возможность добавления сторонних плагинов.

Шина событий, по сути, использует шаблон проектирования «Pub-Sub» (издатель-подписчик). Например, несколько компонентов A, B и C подписываются на событие some.event, а затем определенный компонент D публикует это событие в шине событий, шина событий уведомит всех подписанные компоненты A, B, C о событии, при необходимости передав подписчиками какие-либо данные, отправленные из компонента D.

Итак, вот сам код.

HTML-файл


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Event Bus (шина событий)</title>

    <style>

        html, body {
            margin: 0;
            padding: 0;
            min-height: 100%;
        }

        #app {
            padding: 5px;
            background: #f0f0f0;
            min-height: 100%;
        }

        .row {
            display: flex;
            flex-direction: row;
            flex-wrap: wrap;
            width: 100%;
            gap: 10px;
        }

        .column {
            background: #fff;
            border: 1px solid #ddd;
            display: flex;
            flex-direction: column;
            flex-basis: 100%;
            flex: 1;
            padding: 5px;
        }

        .block {
            min-height: 100px;
        }

        .block_a {

        }

        .block_b {

        }

        #messageLog {
            margin-top: 5px;
            text-align: center;
        }

    </style>
</head>
<body>
    <div id="app">
        <div class="row">
            <div class="column block block_a">
                <button id="button1">Button 1</button>
            </div>
            <div class="column block block_b">
                <button id="button2">Button 2</button>
            </div>
        </div>
        <div class="row">
            <div class="column" id="messageLog">
                This is log message
            </div>
        </div>
    </div>
    <script src="src/main.js" type="module"></script>
</body>
</html>

файл main.js


import EventBus from "./EventBus.js";

/**
 * 
 * @param {*} id 
 * @returns 
 */
const element = id => document.querySelector(id);

// события в отдельном объекте, чтобы не повторять строчки
const EventType = {
    Counter: {
        Increment: "counter.increment", // увеличить счетчик
        Decrement: "counter.decrement"  // уменьшить счетчик
    }
}

function main() 
{
    const btn1 = element('#button1');
    const btn2 = element('#button2');
    const messageLog = element('#messageLog');

    const eventBus = new EventBus();

    // Подписываемся на событие
    eventBus.subscribe(EventType.Counter.Increment, (counter) => {
        messageLog.innerText = "Счетчик: " + counter;
    });

    eventBus.subscribe(EventType.Counter.Decrement, (counter) => {
        messageLog.innerText = "Счетчик: " + counter;
    });

    let counter = 0;

    // при нажатии клавиш отправляем (публикуем) событие с данными в шину
    btn1.addEventListener('click', () => eventBus.publish(EventType.Counter.Increment, ++counter));
    btn2.addEventListener('click', () => eventBus.publish(EventType.Counter.Decrement, --counter));
}

document.addEventListener('DOMContentLoaded', main);

Файл EventBus.js


class EventBus 
{
    constructor()
    {
        // создаем объект, который будет хранить события событий
        this.eventObject = {};
        // идентификатор
        this.callbackId = 0;
    }

    // публикуем событие
    publish(eventName, ...args) 
    {
        // получаем все обработчики прикрепленные к данному событию
        const callback = this.eventObject[eventName];

        if (!callback) return console.warn(eventName + ": событие не найдено!");

        // выполняем каждый обработчик
        for (let id in callback) {
            // передаем параметры обработчику
            callback[id](...args);
        }
    }

    // Подписка на событие
    subscribe(eventName, callback) 
    {
        // инициализируем событие, если оно еще не создано
        if (!this.eventObject[eventName]) {
            this.eventObject[eventName] = {};
        }

        // получаем идентификатор
        const id = this.callbackId++;

        // сохраняем обработчик события подписчика в формате: [имя_события][идентификатор]
        // идентификатор получен выше
        this.eventObject[eventName][id] = callback;

        // Каждый раз, когда вы подписываетесь на событие, генерируется уникальная функция отмены подписки
        const unsubscribe = () => {
            // удаляем обработчик
            delete this.eventObject[eventName][id];

            // Если у этого события нет подписчиков, удаляем вообще все событие
            if (Object.keys(this.eventObject[eventName]).length === 0) {
                delete this.eventObject[eventName];
            }
        };

        return { unsubscribe };
    }
}

export default EventBus;


Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.