Событие является уведомлением , описывающим возникновение недавнего прошлого.
Типичная реализация управляемой событиями системы использует функции диспетчера событий и обработчика (или подписчиков ). Диспетчер предоставляет API для привязки обработчиков к событиям (jQuery's bind
) и метод публикации события своим подписчикам ( trigger
в jQuery). Когда вы говорите о событиях ввода-вывода или пользовательского интерфейса, обычно также есть цикл обработки событий , который обнаруживает новые события, такие как щелчки мыши, и передает их диспетчеру. В JS-land диспетчер и цикл обработки событий обеспечиваются браузером.
Для кода, который напрямую взаимодействует с пользователем - реагируя на нажатия клавиш и щелчки - программирование на основе событий (или его разновидность, например, функционально-реактивное программирование ) практически неизбежно. Вы, программист, понятия не имеете, когда или где пользователь собирается щелкнуть, поэтому вам остается только воспользоваться графическим интерфейсом или браузером для определения действий пользователя в его цикле событий и уведомления вашего кода. Этот тип инфраструктуры также используется в сетевых приложениях (см. NodeJS).
В вашем примере, где вы вызываете событие в своем коде, а не вызываете функцию напрямую, есть несколько более интересных компромиссов, о которых я расскажу ниже. Основное отличие состоит в том, что издатель события ( makeItSnow
) не указывает получателя вызова; это связано в другом месте (в вызове bind
в вашем примере). Это называется « запусти и забудь» : makeItSnow
объявляет миру, что идет снег, но ему все равно, кто слушает, что происходит дальше, или когда это происходит - он просто передает сообщение и отряхивает его руки.
Таким образом, подход, управляемый событиями, разъединяет отправителя сообщения от получателя. Это дает вам одно преимущество: у данного события может быть несколько обработчиков. Вы можете привязать gritRoads
функцию к вашему событию снега, не затрагивая существующий shovelSnow
обработчик. У вас есть гибкость в том, как ваше приложение подключено; чтобы отключить поведение, вам просто нужно удалить bind
вызов, а не искать код, чтобы найти все экземпляры поведения.
Еще одно преимущество программирования, управляемого событиями, заключается в том, что оно дает вам возможность заняться сквозными вопросами. Диспетчер событий играет роль посредника , а некоторые библиотеки (например, Brighter ) используют конвейер, чтобы можно было легко подключать общие требования, такие как ведение журнала или качество обслуживания.
Полное раскрытие: Brighter разработан в Huddle, где я работаю.
Третье преимущество отсоединения отправителя события от получателя заключается в том, что оно дает вам гибкость при обработке события. Вы можете обрабатывать события каждого типа в своем собственном потоке (если ваш диспетчер событий их поддерживает), или вы можете помещать инициированные события в посредник сообщений, такой как RabbitMQ, и обрабатывать их с помощью асинхронного процесса или даже обрабатывать их массово в одночасье. Получатель события может находиться в отдельном процессе или на отдельной машине. Вам не нужно менять код, который вызывает событие, чтобы сделать это! Это большая идея, лежащая в основе «микросервисных» архитектур: автономные сервисы обмениваются данными с использованием событий, а промежуточное ПО для обмена сообщениями является основой приложения.
Для довольно другого примера стиля, управляемого событиями, обратите внимание на дизайн, управляемый доменом , где события домена используются, чтобы помочь сохранить агрегаты отдельными. Например, рассмотрим интернет-магазин, который рекомендует товары на основе вашей истории покупок. A Customer
необходимо обновить историю покупок при ShoppingCart
оплате. ShoppingCart
Агрегат может уведомить Customer
, подняв CheckoutCompleted
событие; Customer
будет обновляться в отдельной транзакции в ответ на событие.
Основным недостатком этой управляемой событиями модели является косвенность. Теперь труднее найти код, который обрабатывает событие, потому что вы не можете просто перейти к нему с помощью IDE; Вы должны выяснить, где событие связано в конфигурации, и надеяться, что вы нашли все обработчики. Есть больше вещей, чтобы держать в голове в любое время. Здесь могут помочь соглашения о стиле кода (например, размещение всех вызовов bind
в одном файле). Ради вашего здравого смысла важно использовать только один диспетчер событий и использовать его последовательно.
Другим недостатком является то, что трудно рефакторинг событий. Если вам нужно изменить формат события, вам также необходимо изменить все получатели. Это усугубляется, когда подписчики события находятся на разных машинах, потому что теперь вам нужно синхронизировать выпуски программного обеспечения!
В определенных обстоятельствах производительность может быть проблемой. При обработке сообщения диспетчер должен:
- Найдите правильные обработчики в некоторой структуре данных.
- Создайте конвейер обработки сообщений для каждого обработчика. Это может включать кучу выделений памяти.
- Динамически вызывать обработчики (возможно, используя отражение, если этого требует язык).
Это, конечно, медленнее, чем обычный вызов функции, который включает в себя только вставку нового кадра в стек. Однако гибкость, которую предоставляет архитектура, управляемая событиями, значительно упрощает изоляцию и оптимизацию медленного кода. Возможность передавать работу на асинхронный процессор - большая победа, поскольку она позволяет вам немедленно обрабатывать запрос, пока тяжелая работа выполняется в фоновом режиме. В любом случае, если вы взаимодействуете с БД или рисуете что-то на экране, тогда затраты на ввод-вывод полностью сократят затраты на обработку сообщения. Это случай предотвращения преждевременной оптимизации.
Таким образом, события являются отличным способом создания слабосвязанного программного обеспечения, но они не без затрат. Например, было бы ошибкой заменять каждый вызов функции в вашем приложении событием. Используйте события, чтобы сделать значимые архитектурные разделения.
$(document).bind('snow', shovelShow)
. Нет необходимости заключать его в анонимную функцию.