Pub / sub, упрощенный

В этой статье мы рассмотрим шаблон публикации / подписки в JavaScript и посмотрим, насколько просто (но эффективно) его можно реализовать в наших приложениях JavaScript.

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

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

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

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

Первое, что мы собираемся сделать, это объявить локальную переменную внутри функции для хранения подписанных обратных вызовов:

function pubSub() {
  const subscribers = {}
}

Затем мы определим subscribe метод, который будет отвечать за вставку обратных вызовов в subscribers:

Здесь происходит следующее: прежде чем пытаться зарегистрировать прослушиватель обратного вызова для имени события, он проверяет, является ли свойство eventName в хранилище subscribers уже значением array. Если это не так, он предполагает, что это будет первый зарегистрированный обратный вызов для subscribers[eventName], и инициализирует его в массив. Затем он отправляет обратный вызов в массив.

Когда срабатывает событие publish, оно принимает два аргумента:

  1. eventName
  2. Любой data, который будет передаваться * каждому обратному вызову, зарегистрированному в subscribers[eventName]

Давайте посмотрим, как это выглядит в коде:

Перед повторением списка обратных вызовов в subscribers он проверяет, действительно ли он существует в виде массива в объекте. Если этого не произойдет, он будет считать, что eventName никогда раньше не регистрировался, поэтому он просто вернется. Это гарантия от возможных сбоев.

После этого, если программа достигает строки .forEach, мы знаем, что eventName был зарегистрирован с одним или несколькими обратными вызовами в прошлом. Программа продолжит безопасный цикл subscribers[eventName].

Для каждого встречного обратного вызова он вызывает обратный вызов с data, который был передан в качестве второго аргумента.

Вот что произойдет, если мы подписались на такую ​​функцию:

function showMeTheMoney(money) {
  console.log(money)
}
const ps = pubSub()
ps.subscribe('show-money', showMeTheMoney)

И если мы вызовем метод publish когда-нибудь в будущем:

ps.publish('show-money', 1000000)

Затем будет вызван зарегистрированный нами обратный вызов showMeTheMoney в дополнение к получению 1000000 в качестве аргумента money:

function showMeTheMoney(money) {
  console.log(money) // result: 10000000
}

Вот как работает шаблон издатель / подписчик. Мы определили функцию pubSub и предоставили локальное местоположение функции, которая хранит обратные вызовы, метод subscribe для регистрации обратных вызовов и метод publish, который выполняет итерацию и вызывает все зарегистрированные обратные вызовы с любыми данными.

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

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

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

Вы также можете сделать что-то вроде этого; однако он менее эффективен:

Недостатки

Хотя у этого шаблона есть огромные преимущества, есть и серьезные недостатки, которые могут стоить нам много времени на отладку.

Как мы узнаем, подписались ли мы на тот же обратный вызов раньше или нет? На самом деле нет никакого способа узнать, если мы не реализуем утилиту, которая отображает список, но тогда мы заставим JavaScript выполнять больше задач.

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

Заключение

На этом мы завершаем этот пост. Я надеюсь, что вы нашли это ценным и ждете большего в будущем!