Am trimis apeluri sau doar am declanșat funcțiile din alte funcții din programele mele pentru a face lucrurile să se întâmple odată cu finalizarea sarcinilor. Când ceva se termină, declanșez funcția direct:
var ground = "clean"; function shovelSnow(){ console.log("Cleaning Snow"); ground = "clean"; } function makeItSnow(){ console.log("It"s snowing"); ground = "snowy"; shovelSnow(); }
Dar eu „am citiți despre multe strategii diferite în programare și una pe care o înțeleg că este puternică, dar pe care nu am practicat-o încă, este bazată pe evenimente (cred că o metodă despre care am citit s-a numit „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);
Aș dori să înțeleg punctele tari și punctele slabe ale evenimentului -programare bazată, comparativ cu apelarea tuturor funcțiilor dvs. din alte funcții. În ce situații de programare are sens utilizarea programării bazate pe evenimente?
Comentarii
Răspuns
Un eveniment este o notificare care descrie o apariție din trecutul recent.
O implementare tipică a unui sistem bazat pe evenimente folosește un dispecer de evenimente și funcții de gestionare (sau abonați ). Dispeceratul oferă un API pentru a conecta gestionarii până la evenimente (jQuery „s bind
) și o metodă de publicare a unui eveniment către abonații săi (trigger
în jQuery). Când vorbiți despre evenimente IO sau UI, există, de obicei, o buclă de evenimente , care detectează evenimente noi, cum ar fi clicuri de mouse și le transmite către dispecer. JS-land, dispeceratul și bucla de evenimente sunt furnizate de browser.
Pentru codul care interacționează direct cu utilizatorul – răspunzând la apăsări de taste și clicuri – programare bazată pe evenimente (sau o variantă a acestora, cum ar fi programare reactivă funcțională ) este aproape inevitabilă. Dumneavoastră, programatorul, nu aveți nicio idee când sau unde va face clic utilizatorul, așa că se află în cadrul GUI sau browser pentru a detecta acțiunea utilizatorului în bucla sa de evenimente și pentru a vă notifica codul. Acest tip de infrastructură este utilizat și în aplicațiile de rețea (cf NodeJS).
Exemplul dvs., în care creați un eveniment în yo codul tău mai degrabă decât apelarea directă a unei funcții are unele compromisuri mai interesante, pe care le voi discuta mai jos. Principala diferență este că editorul unui eveniment (makeItSnow
) nu specifică destinatarul apelului; care s-a conectat în altă parte (în apelul către bind
în exemplul dvs.). Aceasta se numește foc-și-uită : makeItSnow
anunță lumii că ninge, dar nu-i pasă cine ascultă, ce se întâmplă în continuare sau când se întâmplă – transmite pur și simplu mesajul și îi prăfuie mâinile.
Deci abordarea bazată pe evenimente decuplează expeditorul mesajului de la receptor. Un avantaj pe care ți-l oferă este că un anumit eveniment poate avea mai multe gestionare. Ați putea lega o funcție gritRoads
de evenimentul dvs. de zăpadă fără a afecta gestionarul existent shovelSnow
. Aveți flexibilitate în modul în care aplicația dvs. este conectată; pentru a dezactiva un comportament, trebuie doar să eliminați apelul bind
decât să mergeți la vânătoare prin cod pentru a găsi toate instanțele comportamentului.
Un alt avantaj al programarea bazată pe evenimente este că vă oferă undeva unde să vă adresați preocupări transversale. Dispeceratul de evenimente joacă rolul de Mediator , iar unele biblioteci (cum ar fi Brighter ) utilizează o conductă, astfel încât să puteți conecta cu ușurință cerințe generice, cum ar fi jurnalizarea sau calitatea serviciului.
Dezvăluire completă: Brighter este dezvoltat la Huddle, unde lucrez.
Un al treilea avantaj al decuplării expeditorului unui eveniment de receptor este acela că vă oferă flexibilitate în când vă ocupați de eveniment. Puteți procesa fiecare tip de eveniment pe propriul fir (dacă dispecerul dvs. de evenimente îl acceptă) sau puteți pune evenimente ridicate într-un broker de mesaje precum RabbitMQ și gestionați-le cu un proces asincron sau chiar prelucrați-le în bloc peste noapte. Receptorul evenimentului ar putea fi într-un proces separat sau pe o mașină separată. Nu trebuie să schimbați codul care creează evenimentul pentru a face acest lucru! Aceasta este ideea mare din spatele arhitecturilor „microservice”: serviciile autonome comunică folosind evenimente, cu middleware-ul de mesagerie ca coloană vertebrală a aplicației.
Pentru un exemplu destul de diferit de stil bazat pe evenimente, consultați designul bazat pe domeniu, unde evenimente de domeniu sunt folosite pentru a ajuta la menținerea agregatelor separate. De exemplu, luați în considerare un magazin online care recomandă produse bazate pe istoricul achizițiilor dvs. Un Customer
trebuie să aibă actualizat istoricul achizițiilor atunci când se plătește un ShoppingCart
. Agregatul ShoppingCart
ar putea notifica Customer
prin creșterea unui eveniment CheckoutCompleted
; Customer
va fi actualizat într-o tranzacție separată ca răspuns la eveniment.
Principalul dezavantaj al acestui model bazat pe evenimente este indirecția. Acum este mai greu să găsești codul care gestionează evenimentul, deoarece nu poți doar să navighezi către el folosind IDE-ul tău; trebuie să vă dați seama unde este legat evenimentul în configurație și să sperați că ați „găsit toți gestionarii. Există mai multe lucruri de păstrat în cap în orice moment. Convențiile privind stilul de cod vă pot ajuta aici (de exemplu, plasarea tuturor apelurilor către bind
într-un singur fișier). Din motive de sănătate, este important să folosiți un singur dispecerat de evenimente și să îl utilizați în mod consecvent.
Un alt dezavantaj este că este dificil de refactorizat evenimentele. Dacă trebuie să schimbați formatul unui eveniment, trebuie să schimbați și toți receptorii. Acest lucru este agravat atunci când abonații unui eveniment sunt pe diferite mașini, deoarece acum trebuie să sincronizați versiunile software!
În anumite circumstanțe, performanța poate fi o problemă. Când procesează un mesaj, dispecerul trebuie să:
- Căutați gestionarele corecte în unele structuri de date.
- Construiți o conductă de procesare a mesajelor pentru fiecare gestionar. Aceasta poate implica o grămadă de alocări de memorie.
- Apelați dinamic handlerele (eventual folosind reflexia dacă limbajul o cere).
Acest lucru este cu siguranță mai lent decât o funcție obișnuită apel, care implică doar împingerea unui nou cadru pe stivă. Cu toate acestea, flexibilitatea pe care o oferă o arhitectură bazată pe evenimente vă facilitează mult izolarea și optimizarea codului lent. Posibilitatea de a trimite lucrări unui procesor asincron este un mare câștig aici, deoarece vă permite să serviți o cerere imediat în timp ce munca grea este tratată în fundal. În orice caz, dacă interacționați cu DB sau desenați lucruri pe ecran, atunci costurile IO vor crește total costurile procesării unui mesaj. Este un caz de a evita optimizarea prematură.
În rezumat, evenimentele sunt o modalitate excelentă de a construi software cuplat slab, dar nu sunt fără cost. Ar fi o greșeală, de exemplu, să înlocuiți fiecare apel de funcție din aplicația dvs. cu un eveniment. Utilizați evenimente pentru a face diviziuni arhitecturale semnificative.
Comentarii
- Acest răspuns spune același lucru ca 5377 ‘ răspunsul pe care l-am selectat drept corect; ‘ îmi schimb selecția pentru a o marca, deoarece se dezvoltă mai departe.
- Este viteza un dezavantaj semnificativ al codului bazat pe evenimente? Se pare că ar putea fi, dar nu ‘ știu destul de bine.
- @ raptortech97 cu siguranță poate fi. Pentru codul care trebuie să fie deosebit de rapid, probabil că ați dori să evitați trimiterea evenimentelor într-o buclă interioară; din fericire, în astfel de situații, este de obicei bine definit ceea ce trebuie să faceți, așa că nu aveți nevoie ‘ de nevoia extra flexibilă a evenimentelor (sau publicați / abonați sau observatori, care sunt terminologie diferită).
- De asemenea, rețineți că există unele limbi (de exemplu, Erlang) construite în jurul modelului actor în care totul este mesaje (evenimente). În acest caz, compilatorul poate decide dacă implementează mesajele / evenimentele ca apeluri de funcții directe sau ca comunicare.
- Pentru ” performanță ” Cred că trebuie să facem distincția între performanța cu un singur fir și scalabilitatea. Mesajele / evenimentele pot fi mai grave pentru performanța cu un singur thread (dar pot fi convertite în funcții care necesită costuri suplimentare zero și nu sunt mai rele), iar pentru scalabilitate este ‘ superior în aproape fiecare mod (de exemplu, probabil să aibă ca rezultat îmbunătățiri masive ale performanței pe sisteme moderne multi-CPU și viitoare ” many-CPU „).
Răspuns
Programarea bazată pe evenimente este utilizată atunci când programul nu controlează succesiunea evenimentelor pe care le efectuează. În schimb, fluxul de programe este direcționat de un proces extern, cum ar fi un utilizator (de exemplu, GUI), un alt sistem (de exemplu, client / server) sau un alt proces (de exemplu, RPC).
De exemplu, un script de procesare batch știe ce trebuie să facă, așa că doar o face. Nu este nu bazat pe evenimente.
Un procesor de text stă acolo și așteaptă ca utilizatorul să înceapă să scrie.Apăsările de taste sunt evenimente care declanșează funcționalitate pentru actualizarea bufferului intern de documente. Programul nu poate ști ce doriți să tastați, deci trebuie să fie bazat pe evenimente.
Majoritatea programelor GUI sunt bazate pe evenimente, deoarece sunt construite în jurul interacțiunii utilizatorului. Cu toate acestea, programele bazate pe evenimente nu se limitează la GUI, acesta fiind pur și simplu cel mai familiar exemplu pentru majoritatea oamenilor. Serverele web așteaptă conectarea clienților și urmează un mod similar. Procesele de fundal de pe computerul dvs. pot răspunde la evenimente. De exemplu, un scaner antivirus la cerere poate primi un eveniment de la sistemul de operare cu privire la un fișier nou creat sau actualizat, apoi scanează fișierul respectiv pentru viruși.
Răspuns
Într-o aplicație bazată pe evenimente, conceptul de Ascultători de evenimente vă va oferi posibilitatea de a scrie și mai mult Aplicații cuplate în vrac .
De exemplu, un modul sau un plug-in terță parte poate șterge o înregistrare din baza de date și apoi poate declanșa receordDeleted
event și lăsați restul ascultătorilor evenimentului să-și facă treaba. Totul va funcționa bine, chiar dacă modulul de declanșare nici măcar nu știe cine ascultă acest eveniment anume sau ce ar trebui să se întâmple în continuare.
Răspunde
O analogie simplă pe care am vrut să o adaug, care m-a ajutat:
Gândește-te la componentele (sau obiectele) aplicației tale ca la un grup mare de prieteni de pe Facebook.
Când unul dintre prietenii tăi dorește să-ți spună ceva, poate să te sune direct sau să-l posteze pe peretele lor de pe Facebook. Când îl postează pe Facebook-ul lor, atunci oricine l-ar putea vedea și reacționa la el, dar o mulțime de oameni nu. Uneori este ceva important pe care oamenii vor avea nevoie să reacționeze la el, cum ar fi „Avem un copil!” sau „Trupa fulare face un concert surpriză la Drunkin „Bară de scoici!”. În ultimul caz, atunci restul prietenilor vor trebui să reacționeze la aceasta, mai ales dacă sunt interesați de acea bandă.
Dacă prietenul tău vrea să păstreze un secret între tine și ei, probabil nu o postează pe peretele lor de pe Facebook, te sună direct și îți spun. Imaginează-ți un scenariu în care îi spui unei fete care îți place că ai vrea să o întâlnești la un restaurant pentru o întâlnire. În loc să o suni direct și să o întrebi ea, o postezi pe peretele tău de Facebook pentru ca toți prietenii tăi să o vadă. Acest lucru funcționează, dar dacă ai o fostă geloasă, ea ar putea vedea asta și ar putea apărea la restaurant pentru a-ți strica ziua.
Când să decideți dacă să construiți sau nu ascultători de evenimente pentru punerea în aplicare a ceva, gândiți-vă la această analogie. Este nevoie ca această componentă să își pună afacerea acolo pentru ca oricine să o vadă? Sau trebuie să sune pe cineva direct? Lucrurile se pot încurca destul de ușor, așa că atent.
Răspuns
Această analogie următoare vă poate ajuta să înțelegeți programarea I / O bazată pe evenimente, trasând o paralelă cu linia de așteptare la recepția doctorului.
Blocarea I / O este ca și cum, dacă stai la coadă, recepționerul îi cere unui tip din fața ta să completeze formularul și ea așteaptă până când acesta termină. Trebuie să aștepți rândul tău până când tipul își termină formularul, acest lucru se blochează.
Dacă un singur tip durează 3 minute pentru a completa, cel de-al 10-lea tip trebuie să aștepte până la 30 de minute. Acum, pentru a reduce acest al zecelea timp de așteptare a băieților, soluția ar fi, creșterea numărului de recepționere, ceea ce este costisitor. Aceasta se întâmplă în serverele web tradiționale. Dacă solicitați informații despre utilizator, solicitarea ulterioară a altor utilizatori ar trebui să aștepte până la operația curentă, preluarea din baza de date, este finalizată. Acest lucru mărește „timpul de răspuns” al celei de-a 10-a solicitări și crește exponențial pentru al n-lea utilizator. Pentru a evita acest lucru serverele web tradiționale creează fir (echivalent cu numărul tot mai mare de recepționeri) pentru fiecare solicitare , adică, practic creează o copie a serverului pentru fiecare cerere, ceea ce reprezintă intervale costisitoare de consum de procesor, deoarece fiecare cerere va avea nevoie de un fir de sisteme de operare. Pentru a extinde aplicația, ar trebui să aruncați multă putere de calcul către aplicație .
Event Driven : Cealaltă abordare pentru a mări timpul de răspuns al cozii „s” este să alegeți o abordare bazată pe evenimente, unde tipul din coadă va fi predat formular, solicitat să completați și să reveniți la finalizare. Prin urmare, recepționerul poate primi întotdeauna cererea. Este exact ceea ce face javascript de la începutul său. În browser, javascript răspunde la evenimentul de clic al utilizatorului, derulează, glisează sau preluează baza de date și așa mai departe. Acest lucru este posibil în javascript în mod inerent, deoarece javascript tratează funcțiile ca fiind de primă clasă obiectele și acestea pot fi transmise ca parametri la alte funcții (numite apeluri de apel) și pot fi apelate la finalizarea unei anumite sarcini. Aceasta este ceea ce face exact node.js pe server.Puteți găsi mai multe informații despre programarea bazată pe evenimente și blocarea i / o, în contextul nodului aici
$(document).bind('snow', shovelShow)
. Nu este nevoie să o înfășurați într-o funcție anonimă.