Простая шина событий на JavaScript
2 года назад·3 мин. на чтение
Шины событий — это чрезвычайно полезный инструмент для разделения компонентов приложений.
Использование шины событий имеет свои плюсы и минусы. Поэтому она должна добавляться осторожно, иначе вы можете получить код, который будет трудно поддерживать и понимать.
Но нет никаких сомнений в том, что шина событий может значительно ускорить процесс прототипирования или улучшить архитектуру малого и среднего приложения. Большому приложению могут потребоваться некоторые дополнительные инструменты и подходы.
В этой статье рассмотрим как реализовать простую шину событий в JavaScript.
Идентификаторы используются для быстрой идентификации (за время O(1)) подписчика при вызове функции
Что такое шина событий (event bus)?
Шина событий реализует шаблон издатель/подписчик. Его можно использовать для разъединения компонентов приложения, так что компонент может реагировать на события, инициируемые другим компонентом, без их прямых зависимостей друг от друга. Им нужно только знать шину событий. Каждый подписчик может подписаться на определенное событие. Подписчик будет уведомлен, когда событие, на которое он подписан, будет опубликовано в шине событий. Издатель может публиковать события в шине событий, когда что-то происходит.Реализация шины событий
В этой реализации подписчик (subscriber) является простой функцией. Функция вызывается, когда интересующее событие публикуется в шине событий. Для сопоставления события с подписчиками можно использовать простой объект. Формат данных в этом объекте будет следующим:{ eventType: { id: callback } }
. Например:
События{ event1: { 1: func1, 2: func2 }, event2: { 3: func3 } }
event1
и event2
могут быть любого типа. В большинстве случаев имеет смысл использовать простые строки.
Подписчики func1
, func2
и func3
являются простыми JavaScript функциями.func1
и func2
являются подписчиками, подписанными на события типа event1
. func3
является подписчиком, подписанным на события типа event2
.
Идентификаторы 1
, 2
и 3
будут использоваться позже для отмены подписки на подписчиков.
Как подписаться на событие?
Функцияsubscribe
принимает в качестве аргументов интересующее событие и подписчика.
Функция возвращает объект, предоставляющий функцию отмены подписки - unsubscribe
. Функция unsubscribe
может быть вызвана для удаления зарегистрированного подписчика.
В этом примере используем генератор id для получения уникальных идентификаторов.Идентификаторы используются для быстрой идентификации (за время O(1)) подписчика при вызове функции
unsubscribe
.
Вместо использования генератора id можно рассмотреть реализацию на основе ES6 символов.
const subscriptions = {} const getNextUniqueId = getIdGenerator() function subscribe(eventType, callback) { const id = getNextUniqueId() // создаем новый элемент для eventType if(!subscriptions[eventType]) { subscriptions[eventType] = {} } // регистрируем функцию обратного вызова subscriptions[eventType][id] = callback return { unsubscribe: () => { delete subscriptions[eventType][id] if(Object.keys(subscriptions[eventType]).length === 0) { delete subscriptions[eventType] } } } }
Как опубликовать событие?
Функция публикации принимает в качестве аргументов событие и аргументы для подписчиков. Если подписчиков наeventType
нет, то просто завершаем функцию.
В противном случае перебираются идентификаторы подписчиков, зарегистрированных для eventType
, и вызывается каждая функция с предоставленными аргументами.
function publish(eventType, arg) { if(!subscriptions[eventType]) { return } Object.keys(subscriptions[eventType]) .forEach(id => subscriptions[eventType][id](arg)) }
Пример использования
В качестве простого примера предположим, что мы хотим печатать что-то в консоли каждый раз, когда появляется событие типаprint
.
Мы можем подписаться на событие следующим образом:
Затем мы можем выпустить событие, подобное этому:const subscription = EventBus.subscribe( "print", message => console.log(`printing: ${message}`) )
Если в какой-то момент мы захотим прекратить прослушивание события с типомEventBus.publish("print", "some text")
print
, мы можем отписаться следующим образом:
subscription.unsubscribe()
Полная реализация
Вот полная реализация этой шины событий. Он может быть легко преобразован в простую функцию Javascript.// eventBus.js const subscriptions = {} const getNextUniqueId = getIdGenerator() function subscribe(eventType, callback) { const id = getNextUniqueId() if(!subscriptions[eventType]) { subscriptions[eventType] = {} } subscriptions[eventType][id] = callback return { unsubscribe: () => { delete subscriptions[eventType][id] if(Object.keys(subscriptions[eventType]).length === 0) { delete subscriptions[eventType] } } } } function publish(eventType, arg) { if(!subscriptions[eventType]) { return } Object.keys(subscriptions[eventType]).forEach(key => subscriptions[eventType][key](arg)) } function getIdGenerator() { let lastId = 0 return function getNextUniqueId() { lastId += 1 return lastId } } module.exports = { publish, subscribe }
Как создать массив, заполненный числами в JavaScript
2 года назад·1 мин. на чтение
Иногда нужно создать массив заполненный числами в JavaScript или создать массив от 1 до определённого числа. В этой статье содержится короткий ответ для быстрого решения.
Короткое решение
Для получения массива чисел от 0 до N, можно воспользоваться методом массиваfrom
.
Array.from({ length: 10 }, (_, i) => i) // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Более универсальный способ в виде функции
length
- длина массива. fn
- функция для кастомизации элемента. Т.к. числа по умолчанию начинаются с нуля, мы можем передать i => i + 1
и получить массив от 1 до N, как в примере ниже.
Использованиеconst getNumbers = (length, fn = i => i) => { return Array.from({ length }, (_, i) => fn(i)) }
console.log(getNumbers(10, i => i + 1)) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] console.log(getNumbers(10)) // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Реверсированный массив
Здесь в функцию передается не простоlength
, а length - 1
. Это делается для того, чтобы сохранить соглашение, что индексы начинаются с 0
. См. пример ниже.
Использованиеconst getNumbersReversed = (length, fn = i => i ) => { return Array.from({ length }, (_, i) => fn(length - 1 - i)) }
console.log(getNumbersReversed(10, i => i + 1)) // [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] console.log(getNumbersReversed(10)) // [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]