Przekazywałem wywołania zwrotne lub po prostu uruchamiałem funkcje z innych funkcji w moich programach, aby coś się stało po zakończeniu zadań. Kiedy coś się kończy, uruchamiam funkcję bezpośrednio:

 var ground = "clean"; function shovelSnow(){ console.log("Cleaning Snow"); ground = "clean"; } function makeItSnow(){ console.log("It"s snowing"); ground = "snowy"; shovelSnow(); }  

Ale ja „ve czytać o wielu różnych strategiach w programowaniu, a jedna, którą rozumiem jako potężną, ale jeszcze nie przećwiczyłem, jest oparta na zdarzeniach (myślę, że metoda, o której czytałem, nazywała się „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);  

Chciałbym zrozumieć obiektywne mocne i słabe strony wydarzenia programowanie oparte na programowaniu, a nie tylko wywoływanie wszystkich funkcji z poziomu innych funkcji. W jakich sytuacjach programistycznych programowanie oparte na zdarzeniach ma sens?

Komentarze

  • Na marginesie możesz po prostu użyć $(document).bind('snow', shovelShow). Nie ma potrzeby umieszczania go w anonimowej funkcji.
  • Możesz także chcieć poznać ” programowanie reaktywne „, co ma wiele wspólnego z programowaniem sterowanym zdarzeniami.

Odpowiedź

Zdarzenie to powiadomienie opisujące zdarzenie z niedawnej przeszłości.

Typowa implementacja systemu sterowanego zdarzeniami wykorzystuje dyspozytor zdarzeń i funkcje obsługi (lub subskrybenci ). Dyspozytor zapewnia interfejs API do obsługi połączeń aż do zdarzeń (jQuery „s bind) oraz metodę publikowania zdarzenia do jego subskrybentów (trigger w jQuery). Kiedy mówisz o zdarzeniach IO lub UI, zazwyczaj jest też pętla zdarzeń , która wykrywa nowe zdarzenia, takie jak kliknięcia myszą i przekazuje je do dyspozytora. JS-land, dyspozytor i pętla zdarzeń są udostępniane przez przeglądarkę.

W przypadku kodu, który oddziałuje bezpośrednio z użytkownikiem – reaguje na naciśnięcia klawiszy i kliknięcia – programowanie sterowane zdarzeniami (lub jego odmiana, na przykład reaktywne programowanie funkcjonalne ) jest prawie nieuniknione. Ty, programista, nie masz pojęcia, kiedy i gdzie użytkownik kliknie, więc wszystko zależy od frameworka GUI lub przeglądarkę, aby wykryć działanie użytkownika w pętli zdarzeń i powiadomić Twój kod. Ten typ infrastruktury jest również używany w aplikacjach sieciowych (por. NodeJS).

Twój przykład, w którym zgłaszasz zdarzenie w yo Twój kod zamiast bezpośredniego wywoływania funkcji ma kilka bardziej interesujących kompromisów, które omówię poniżej. Główna różnica polega na tym, że wydawca zdarzenia (makeItSnow) nie określa odbiorcy wywołania; który „jest podłączony w innym miejscu (w wywołaniu do bind w twoim przykładzie). Nazywa się to uruchom i zapomnij : makeItSnow ogłasza światu, że pada śnieg, ale nie obchodzi go, kto słucha, co się dzieje dalej ani kiedy to się dzieje – po prostu przekazuje wiadomość i odkurza ręce.


Zatem podejście oparte na zdarzeniach oddziela nadawcę wiadomości od odbiorcy. Jedną z zalet, jakie to zapewnia, jest to, że dane zdarzenie może mieć wiele programów obsługi. Możesz powiązać funkcję gritRoads ze zdarzeniem śniegu bez wpływu na istniejącą procedurę obsługi shovelSnow. Masz elastyczność w sposobie okablowania aplikacji; Aby wyłączyć zachowanie, wystarczy usunąć wywołanie bind, zamiast przeszukiwać kod, aby znaleźć wszystkie wystąpienia tego zachowania.

Kolejna zaleta Programowanie sterowane zdarzeniami polega na tym, że daje miejsce na przekrojowe problemy. Dyspozytor zdarzeń pełni rolę Mediatora , a niektóre biblioteki (takie jak Jaśniej ) wykorzystują potok, dzięki któremu można łatwo podłączyć ogólne wymagania, takie jak logowanie lub jakość usług.

Pełne ujawnienie: Brighter jest rozwijany w Huddle, gdzie pracuję.

Trzecią zaletą oddzielenia nadawcy zdarzenia od odbiorcy jest to, że zapewnia elastyczność w czasie obsługi zdarzenia. Możesz przetwarzać zdarzenia każdego typu we własnym wątku (jeśli Twój dyspozytor zdarzeń to obsługuje) lub możesz umieścić zgłoszone zdarzenia w brokerze komunikatów, takim jak RabbitMQ i obsługiwać je w procesie asynchronicznym lub nawet przetwarzać zbiorczo w ciągu nocy. Odbiorca zdarzenia może znajdować się w oddzielnym procesie lub na oddzielnym komputerze. Nie musisz zmieniać kodu, który wywołuje zdarzenie, aby to zrobić! Oto wielka idea architektur „mikrousług”: usługi autonomiczne komunikują się za pomocą zdarzeń, a podstawą aplikacji jest oprogramowanie pośredniczące do przesyłania wiadomości.

Dość inny przykład stylu opartego na zdarzeniach można znaleźć w projekcie opartym na domenie, gdzie zdarzenia w domenie służą do oddzielania agregatów. Weźmy na przykład pod uwagę sklep internetowy, który poleca produkty na podstawie historii zakupów. Customer musi mieć aktualizowaną historię zakupów po opłaceniu ShoppingCart. Agregat ShoppingCart może powiadomić Customer, podnosząc zdarzenie CheckoutCompleted; Customer zostanie zaktualizowany w oddzielnej transakcji w odpowiedzi na zdarzenie.


Główną wadą tego modelu sterowanego zdarzeniami jest pośredni. Obecnie trudniej jest znaleźć kod obsługujący zdarzenie, ponieważ nie można po prostu przejść do niego za pomocą swojego IDE; musisz dowiedzieć się, gdzie zdarzenie jest powiązane w konfiguracji i mieć nadzieję, że znalazłeś wszystkie programy obsługi. W każdej chwili jest więcej rzeczy do zapamiętania. Mogą tu pomóc konwencje stylów kodu (na przykład umieszczenie wszystkich wywołań bind w jednym pliku). Ze względu na swój rozsądek ważne jest, aby używać tylko jednego modułu rozsyłającego zdarzenia i używać go konsekwentnie.

Kolejną wadą jest to, że trudno jest refaktoryzować zdarzenia. Jeśli chcesz zmienić format wydarzenia, musisz również zmienić wszystkie odbiorniki. Sytuacja pogarsza się, gdy subskrybenci wydarzenia znajdują się na różnych komputerach, ponieważ teraz musisz zsynchronizować wersje oprogramowania!

W pewnych okolicznościach wydajność może być problemem. Podczas przetwarzania wiadomości dyspozytor musi:

  1. Wyszukać poprawne procedury obsługi w jakiejś strukturze danych.
  2. Zbudować potok przetwarzania wiadomości dla każdego modułu obsługi. Może to wymagać alokacji pamięci.
  3. Wywołaj dynamicznie programy obsługi (prawdopodobnie używając refleksji, jeśli język tego wymaga).

Jest to z pewnością wolniejsze niż zwykła funkcja wywołanie, które polega tylko na umieszczeniu nowej ramki na stosie. Jednak elastyczność, jaką zapewnia architektura sterowana zdarzeniami, znacznie ułatwia izolowanie i optymalizowanie wolnego kodu. Możliwość przesłania pracy do procesora asynchronicznego jest tutaj dużą zaletą, ponieważ umożliwia natychmiastową obsługę żądania, podczas gdy ciężka praca jest wykonywana w tle. W każdym razie, jeśli „wchodzisz w interakcję z bazą danych lub rysujesz rzeczy na ekranie, koszty IO całkowicie spowodują spadek kosztów przetwarzania wiadomości”. Jest to przypadek uniknięcia przedwczesnej optymalizacji.


Podsumowując, wydarzenia to świetny sposób na tworzenie luźno powiązanych programów, ale nie są one pozbawione kosztów. Błędem byłoby na przykład zastąpienie każdego wywołania funkcji w aplikacji zdarzeniem. Użyj wydarzeń, aby dokonać znaczących podziałów architektonicznych.

Komentarze

  • Ta odpowiedź jest taka sama jak 5377 ' odpowiedź, którą wybrałem jako poprawną; ' m zmieniam swój wybór, aby zaznaczyć ten, ponieważ jest to bardziej szczegółowe.
  • Czy szybkość jest istotną wadą kodu sterowanego zdarzeniami? Wygląda na to, że mogłoby tak być, ale nie ' nie wiem zbytnio.
  • @ raptortech97 z pewnością tak. W przypadku kodu, który musi być szczególnie szybki, prawdopodobnie chciałbyś uniknąć wysyłania zdarzeń w pętli wewnętrznej; na szczęście w takich sytuacjach zwykle jest dobrze zdefiniowane, co musisz zrobić, więc nie ' nie potrzebujesz dodatkowej elastyczności wydarzeń (lub publikowania / subskrybowania lub obserwatorów, które są równoważnymi mechanizmami z inna terminologia).
  • Należy również zauważyć, że istnieją języki (np. Erlang) zbudowane wokół modelu aktora, w którym wszystko jest komunikatami (wydarzeniami). W tym przypadku kompilator może zdecydować, czy zaimplementować komunikaty / zdarzenia jako bezpośrednie wywołania funkcji, czy jako komunikację.
  • Dla ” wydajności ” Myślę, że musimy odróżnić wydajność jednowątkową od skalowalności. Komunikaty / zdarzenia mogą być gorsze w przypadku wydajności jednowątkowej (ale można je przekształcić w wywołania funkcji bez dodatkowych kosztów i nie gorsze), a ze względu na skalowalność ' są lepsze praktycznie w każdym sposób (np. może spowodować ogromną poprawę wydajności w nowoczesnych systemach wieloprocesorowych i przyszłych ” wieloprocesorowych ” systemach).

Odpowiedź

Programowanie oparte na zdarzeniach jest używane, gdy program nie kontroluje sekwencji zdarzeń, które wykonuje. Zamiast tego przepływ programu jest kierowany przez proces zewnętrzny, taki jak użytkownik (np. GUI), inny system (np. Klient / serwer) lub inny proces (np. RPC).

Na przykład skrypt przetwarzania wsadowego wie, co musi zrobić, więc po prostu to robi. Jest nie oparty na zdarzeniach.

Znajduje się tam edytor tekstu i czeka, aż użytkownik zacznie pisać.Naciśnięcia klawiszy to zdarzenia, które uruchamiają funkcję aktualizacji wewnętrznego bufora dokumentów. Program nie może wiedzieć, co chcesz wpisać, więc musi być sterowany zdarzeniami.

Większość programów GUI jest sterowana zdarzeniami, ponieważ są zbudowane wokół interakcji użytkownika. Jednak programy oparte na zdarzeniach nie są ograniczone do GUI, jest to po prostu najbardziej znany przykład dla większości ludzi. Serwery WWW czekają na połączenie klientów i postępują zgodnie z podobnym idiomem. Procesy działające w tle na komputerze również mogą reagować na zdarzenia. Na przykład skaner antywirusowy na żądanie może otrzymać zdarzenie z systemu operacyjnego dotyczące nowo utworzonego lub zaktualizowanego pliku, a następnie przeskanować ten plik w poszukiwaniu wirusów.

Odpowiedź

W aplikacji opartej na zdarzeniach koncepcja nasłuchiwania zdarzeń daje możliwość pisania jeszcze większej liczby Aplikacje luźno powiązane .

Na przykład moduł lub wtyczka innej firmy może usunąć rekord z bazy danych, a następnie wywołać receordDeleted event, a resztę pozostaw odbiorcom zdarzeń, aby wykonali swoją pracę. Wszystko będzie działać dobrze, nawet jeśli moduł wyzwalający nie wie nawet, kto nasłuchuje tego konkretnego zdarzenia ani co powinno się wydarzyć dalej.

Odpowiedź

Prosta analogia, którą chciałem dodać, pomogła mi:

Pomyśl o komponentach (lub obiektach) swojej aplikacji jak o dużej grupie znajomych na Facebooku.

Gdy któryś z Twoich znajomych chce Ci coś powiedzieć, może zadzwonić do Ciebie bezpośrednio lub zamieścić to na swojej tablicy na Facebooku. Gdy publikują to na swoim Facebooku, każdy mógłby to zobaczyć i zareagować, ale wiele osób nie „t. Czasami jest to coś ważnego, na co ludzie prawdopodobnie będą musieli zareagować, np.” Mamy dziecko! „lub” Taki a taki zespół daje niespodziewany koncert w Drunkin „Batonik z małżami!”. W ostatnim przypadku reszta znajomych prawdopodobnie będzie musiała na to zareagować, zwłaszcza jeśli są zainteresowani tym zespołem.

Jeśli twój przyjaciel chce zachować tajemnicę między tobą a nimi, prawdopodobnie nie opublikowałby tego na swojej tablicy na Facebooku, zadzwoniliby bezpośrednio do Ciebie i powiedzieliby. Wyobraź sobie scenariusz, w którym mówisz dziewczynie, że lubisz, że chciałbyś się z nią spotkać w restauracji na randkę. Zamiast dzwonić bezpośrednio do niej i pytać ją, umieszczasz to na swojej ścianie na Facebooku, aby wszyscy mogli je zobaczyć. To działa, ale jeśli masz zazdrosnego byłego, może to zobaczyć i pojawić się w restauracji, aby zrujnować Ci dzień.

Kiedy decydując, czy tworzyć odbiorniki zdarzeń w celu implementacji czegoś, zastanów się nad tą analogią. Czy ten komponent musi pokazać swoją firmę, aby każdy mógł ją zobaczyć? A może musi zadzwonić do kogoś bezpośrednio? Sprawy mogą się dość łatwo skomplikować, więc ostrożnie.

Odpowiedź

Poniższa analogia może pomóc u zrozumieć programowanie we / wy sterowane zdarzeniami poprzez narysowanie linii równoległej do linii oczekiwania w recepcji lekarza.

Blokowanie I / O to tak, jakbyś stał w kolejce, recepcjonistka prosi faceta przed tobą o wypełnienie formularza, a ona czeka, aż skończy. Musisz poczekać na swoją kolej, aż facet skończy swoją formę, to jest blokowanie.

Jeśli wypełnienie pojedynczego faceta zajmuje 3 minuty, dziesiąty musi czekać do 30 minut. Teraz, aby skrócić ten dziesiąty czas oczekiwania, rozwiązaniem byłoby zwiększenie liczby recepcjonistów, co jest kosztowne. Tak dzieje się w tradycyjnych serwerach internetowych. Jeśli poprosisz o informacje o użytkowniku, kolejne żądanie innych użytkowników powinno poczekać do bieżąca operacja pobierania z bazy danych jest zakończona. Zwiększa to „czas odpowiedzi” na 10. żądanie i wydłuża się wykładniczo dla n-tego użytkownika. Aby tego uniknąć, tradycyjne serwery WWW tworzą wątek (równoważny zwiększającej się liczbie recepcjonistów) dla każdego pojedynczego żądania tj. w zasadzie tworzy kopię serwera dla każdego żądania, co wiąże się z kosztownym zużyciem procesora, ponieważ każde żądanie będzie wymagało wątku systemów operacyjnych. Aby skalować aplikację, musiałbyś rzucić dużo mocy obliczeniowej na aplikację .

Sterowany zdarzeniami : Innym podejściem do skalowania w górę „s” czasu odpowiedzi ”kolejki jest wybierz podejście oparte na zdarzeniach, w którym facet w kolejce otrzyma plik formularz, poproszony o wypełnienie i powrót po zakończeniu. Dlatego recepcjonistka zawsze może przyjąć prośbę. To jest dokładnie to, co robił javascript od momentu jego powstania. W przeglądarce javascript reagowałby na zdarzenie kliknięcia użytkownika, przewijanie, przesunięcie lub pobieranie bazy danych itd. Jest to możliwe w javascript z natury, ponieważ javascript traktuje funkcje jako pierwszą klasę obiekty i mogą być przekazywane jako parametry do innych funkcji (zwanych callbackami) i mogą być wywoływane po zakończeniu określonego zadania.To właśnie robi node.js na serwerze.Więcej informacji na temat programowania sterowanego zdarzeniami i blokowania wejść / wyjść w kontekście węzła tutaj

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *