Коли я повинен використовувати програмування на основі подій?


65

Я передавав зворотні дзвінки або просто запускав функції з інших функцій у своїх програмах, щоб зробити це після того, як завдання будуть виконані. Коли щось закінчується, я запускаю функцію безпосередньо:

var ground = 'clean';

function shovelSnow(){
    console.log("Cleaning Snow");
    ground = 'clean';
}

function makeItSnow(){
    console.log("It's snowing");
    ground = 'snowy';
    shovelSnow();
}

Але я читав про багато різних стратегій програмування, і одна, яку я вважаю потужною, але ще не практикувала, заснована на подіях (думаю, метод, про який я читав, називався "pub-sub" ):

var ground = 'clean';

function shovelSnow(){
    console.log("Cleaning Snow");
    ground = 'clean';
}

function makeItSnow(){
    console.log("It's snowing");
    ground = 'snowy';
    $(document).trigger('snow');
}

$(document).bind('snow', shovelSnow);

Я хотів би зрозуміти об'єктивні сильні та слабкі сторони програмування на основі подій, а саме просто виклик усіх ваших функцій з інших функцій. У яких ситуаціях програмування має сенс використовувати програмування на основі подій?


2
Як сторону, ви можете просто використовувати $(document).bind('snow', shovelShow). Не потрібно загортати його в анонімну функцію.
Карл Білефельдт

4
Можливо, вам також буде цікаво дізнатися про "реактивне програмування", яке має багато спільного з програмованим на події програмою.
Ерік Ліпперт

Відповіді:


75

Подія є повідомленням , що описує виникнення недавнього минулого.

Типова реалізація системи, керованої подіями, використовує функції диспетчера подій та обробника подій (або передплатників ). Диспетчер надає API для підключення обробників до подій (jQuery's bind) та метод публікації події своїм підписникам ( triggerу jQuery). Коли ви говорите про події IO або UI, зазвичай також існує цикл подій , який виявляє нові події, такі як клацання мишкою та передає їх диспетчеру. У JS-land диспетчер та цикл подій надаються браузером.

Що стосується коду, який взаємодіє безпосередньо з користувачем - реагуючи на натискання клавіш і клацання - програмування, орієнтоване на події (або його різновиди, такі як функціональне реактивне програмування ) майже неминуче. Ви, програміст, не знаєте, коли або де користувач збирається натиснути, тому вниз або в рамках графічного інтерфейсу, або в браузері, виявити дії користувача в його циклі подій та повідомити про свій код. Цей тип інфраструктури також використовується в мережевих додатках (див. NodeJS).

Ваш приклад, в якому ви піднімаєте подію у своєму коді, а не викликаєте функцію безпосередньо, має ще кілька цікавих компромісів, про які я розповім нижче. Основна відмінність полягає в тому, що видавець події ( makeItSnow) не вказує приймача виклику; що підключено в іншому місці (у заклику до bindвашого прикладу). Це називається " забути і забути" : makeItSnowсповіщає світові про те, що йде сніг, але байдуже, хто слухає, що буде далі або коли це станеться - він просто передає повідомлення і пиляє з рук.


Тож підхід, керований подією, відокремлює відправника повідомлення від одержувача. Однією з переваг, яку це надає вам, є те, що дана подія може мати декілька обробників. Ви можете прив’язати gritRoadsфункцію до своєї снігової події, не впливаючи на існуючий shovelSnowобробник. У вас є гнучкість у способі підключення вашої програми; Щоб вимкнути поведінку, потрібно просто зняти bindвиклик, а не піти на полювання за кодом, щоб знайти всі випадки поведінки.

Ще одна перевага програмування, орієнтованого на події, полягає в тому, що він дає змогу десь поставити наскрізні проблеми. Диспетчер подій відіграє роль Посередника , а деякі бібліотеки (наприклад, Brighter ) використовують конвеєр, щоб ви могли легко підключати загальні вимоги, такі як реєстрація або якість обслуговування.

Повне розкриття інформації: Яскравіший розробляється в компанії Huddle, де я працюю.

Третя перевага розв'язки відправника події від приймача є те , що вона дає вам гнучкість в коли ви обробляти подія. Ви можете обробити кожен тип подій у власному потоці (якщо ваш диспетчер подій підтримує його), або ви можете помістити підняті події на брокера повідомлень, наприклад RabbitMQ, і обробити їх асинхронним процесом або навіть обробити їх масово протягом ночі. Одержувач події може перебувати в окремому процесі або на окремій машині. Вам не потрібно змінювати код, який викликає подію для цього! Це велика ідея архітектури "мікросервісних": автономні сервіси спілкуються за допомогою подій, а посередництво програми обміну повідомленнями є основою програми.

Для досить іншого прикладу стилю, керованого подіями, зверніться до дизайну, керованого доменом , де події домену використовуються, щоб допомогти зберегти агрегати окремими. Наприклад, розглянемо Інтернет-магазин, який рекомендує товари на основі історії ваших покупки. CustomerПовинен мати свою історію покупок оновлюється , коли ShoppingCartоплачується. ShoppingCartАгрегат може повідомити Customer, піднявши CheckoutCompletedподія; Customerбуде оновлюватися в окремій транзакції у відповідь на подію.


Основним недоліком цієї моделі, керованої подіями, є непрямість. Зараз важче знайти код, який обробляє подію, оскільки ви не можете просто перейти до нього за допомогою свого IDE; ви повинні з'ясувати, де подія пов'язана в конфігурації, і сподіватися, що ви знайшли всіх обробників. Є більше речей, які потрібно тримати в голові в будь-який час. Тут можуть допомогти умови стилів коду (наприклад, розміщення всіх дзвінків bindв одному файлі). Заради вашого розуму важливо використовувати лише один диспетчер подій і послідовно використовувати його.

Ще одним недоліком є ​​те, що важко відремонтувати події. Якщо вам потрібно змінити формат події, вам також потрібно змінити всі приймачі. Це посилюється, коли передплатники події знаходяться на різних машинах, адже тепер вам потрібно синхронізувати версії програм!

За певних обставин результативність може викликати занепокоєння. Під час обробки повідомлення диспетчер повинен:

  1. Знайдіть правильні обробники в деякій структурі даних.
  2. Побудуйте конвеєр обробки повідомлень для кожного обробника. Це може включати купу розподілу пам'яті.
  3. Динамічно викликайте обробників (можливо, використовуючи роздуми, якщо мова цього вимагає).

Це, звичайно, повільніше, ніж звичайний виклик функції, який передбачає лише натискання нового кадру на стек. Однак гнучкість, яку надає вам орієнтована на події архітектура, значно полегшує ізоляцію та оптимізацію повільного коду. Наявність можливості подати роботу в асинхронний процесор - це велика виграш, оскільки це дозволяє негайно обслуговувати запит, коли важку роботу вирішують у фоновому режимі. У будь-якому випадку, якщо ви взаємодієте з БД або малюєте речі на екрані, то витрати на IO повністю перекриють витрати на обробку повідомлення. Це уникнення передчасної оптимізації.


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


2
Ця відповідь говорить так само, як відповідь 5377, яку я вибрав як правильну; Я змінюю свій вибір, щоб відзначити цей, оскільки він детальніше розробляє.
Візіонер

1
Чи швидкість є істотним недоліком коду, керованого подіями? Здається, це могло бути, але я не зовсім знаю.
raptortech97

1
@ raptortech97 це, безумовно, може бути. Щодо коду, який повинен бути особливо швидким, ви, ймовірно, хочете уникати надсилання подій у внутрішній цикл; на щастя, в таких ситуаціях зазвичай добре визначено те, що вам потрібно зробити, тому вам не потрібно зайвої гнучкості подій (або публікації / підписки або спостерігачів, які є рівнозначними механізмами з різною термінологією).
Жуль

1
Також зауважте, що існують деякі мови (наприклад, Erlang), побудовані навколо моделі актора, де все є повідомленнями (подіями). У цьому випадку компілятор може вирішити, чи реалізовувати повідомлення / події як прямі виклики функцій або як комунікації.
Брендан

1
Для "продуктивності" я думаю, що нам потрібно розрізняти однопоточні показники та масштабованість. Повідомлення / події можуть бути гіршими для однопотокової продуктивності (але їх можна перетворити на функціональні виклики за нульову додаткову вартість і бути не гіршими), а щодо масштабованості - це практично практично в будь-який спосіб (наприклад, це може призвести до значних поліпшень продуктивності на сучасних мульти -CPU та майбутні системи "багатьох ЦП").
Брендан

25

Програмування на основі подій використовується, коли програма не контролює послідовність подій, які вона виконує. Натомість потік програми спрямований зовнішнім процесом, таким як користувач (наприклад, GUI), інша система (наприклад, клієнт / сервер) або інший процес (наприклад, RPC).

Наприклад, сценарій пакетної обробки знає, що потрібно зробити, щоб він просто це зробив. Це не на основі подій.

Текстовий процесор сидить там і чекає, коли користувач почне вводити текст. Натискання клавіш - це події, які викликають функціональність для оновлення внутрішнього буфера документа. Програма не може знати, що ви хочете ввести, тому вона повинна керуватися подіями.

Більшість програм GUI керуються подіями, оскільки вони побудовані на основі взаємодії користувачів. Однак програми на основі подій не обмежуються графічними інтерфейсами, це просто найвідоміший приклад більшості людей. Веб-сервери чекають, коли клієнти підключаться і слідкують за подібною фразою. Фонові процеси на вашому комп'ютері можуть також реагувати на події. Наприклад, сканер вірусів на вимогу може отримати події від ОС щодо новоствореного або оновленого файлу, а потім просканувати цей файл на наявність вірусів.


18

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

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


6

Проста аналогія, яку я хотів додати, що допомогла мені:

Подумайте про компоненти (або об’єкти) вашої програми як про велику групу друзів у Facebook.

Коли хтось із ваших друзів хоче щось сказати вам, він може або зателефонувати вам безпосередньо, або опублікувати його на своїй стіні у Facebook. Коли вони публікують це у своєму Facebook, тоді хтось може бачити це і реагувати на нього, але багато людей цього не роблять. Іноді це щось важливе, що людям, ймовірно, доведеться реагувати на це, як-от "У нас є дитина!" або "Група" Так і так "проводить сюрпризний концерт у барі" Друккі Клам "!". В останньому випадку тоді іншим друзям, ймовірно, доведеться на це відреагувати, особливо якщо вони зацікавлені в цьому гурті.

Якщо ваш друг хоче зберегти таємницю між вами та ними, вони, ймовірно, не опублікують його на своїй стіні у Facebook, вони зателефонують вам безпосередньо та скажуть вам. Намалюйте сценарій, коли ви кажете дівчині, яка вам подобається, що ви хочете зустріти її в ресторані на побачення. Замість того, щоб зателефонувати їй безпосередньо і запитати її, ви розміщуєте це на стіні у Facebook, щоб усі ваші друзі могли бачити. Це працює, але якщо у вас є ревнива екс, то вона могла це побачити і з'явитися в ресторані, щоб зіпсувати вам день.

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


0

Ця наступна аналогія може допомогти вам зрозуміти програмування вводу-виводу, керованого подіями, провівши паралельно лінії очікування на стійці прийому лікарів.

Блокування вводу-виводу подібне, якщо ви стоїте в черзі, портьє просить хлопця перед вами заповнити форму, і вона чекає, поки він закінчить. Вам доведеться чекати своєї черги, поки хлопець не закінчить свою форму, це блокується.

Якщо один хлопець потребує 3 хвилини, щоб заповнити, 10-му хлопцеві доведеться почекати до 30 хвилин. Тепер для скорочення цього десятого хлопця час очікування, рішення було б, збільшення кількості приймальників, що коштує дорого. Це відбувається на традиційних веб-серверах. Якщо ви запитаєте інформацію про користувача, подальший запит інших користувачів повинен зачекати, поки завершиться поточна операція, отримана з бази даних. Це збільшує "час на відповідь" 10-го запиту і збільшується експоненціально для n-го користувача. Щоб уникнути цього традиційні веб-сервери створюють потік (еквівалентний збільшенню кількості приймачів) для кожного окремого запиту, тобто, в основному, він створює копію сервера для кожного запиту, що є дорогим інтервалом споживання процесора, оскільки кожен запит потребує Операційних систем нитка. Щоб масштабувати додаток,

Керований подією : Іншим підходом до збільшення "часу відповіді" черги є перехід на підхід, керований подіями, коли хлопцеві в черзі будуть передані форми, запропоновані заповнити і повернутися після завершення. Отже, приймальня завжди може приймати запит. Саме цим займається JavaScript з моменту його створення. У веб-переглядачі javascript реагує на події користувача, натискання, проведіть пальцем або виберіть базу даних тощо. Це можливо в JavaScript по суті, тому що JavaScript розглядає функції як об'єкти першого класу, і вони можуть передаватися як параметри іншим функціям (званим зворотними дзвінками) і можуть бути викликані при виконанні конкретного завдання. Це саме те, що саме node.js робить на сервері. Ви можете знайти більше інформації про програмування на основі подій та блокування вводу-виводу в контексті вузла тут

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.