Jeg har sendt tilbagekald eller bare udløst funktionerne fra en anden funktion i mine programmer for at få ting til at ske, når opgaverne er gennemført. Når noget er færdigt, udløser jeg funktionen direkte:
var ground = "clean"; function shovelSnow(){ console.log("Cleaning Snow"); ground = "clean"; } function makeItSnow(){ console.log("It"s snowing"); ground = "snowy"; shovelSnow(); }
Men jeg har læse om mange forskellige strategier i programmering, og en, som jeg forstår er stærk, men endnu ikke har praktiseret, er begivenhedsbaseret (jeg tror, en metode, jeg læste om, blev kaldt “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);
Jeg vil gerne forstå de objektive styrker og svagheder ved begivenheden -baseret programmering, vs bare at ringe til alle dine funktioner fra andre funktioner. I hvilke programmeringssituationer giver begivenhedsbaseret programmering mening at bruge?
Kommentarer
Svar
En begivenhed er en meddelelse, der beskriver en begivenhed fra den nylige fortid.
En typisk implementering af et hændelsesdrevet system bruger en hændelsesforsendelse og handlerfunktioner (eller abonnenter ). Afsenderen tilvejebringer en API til at lede håndterere op til begivenheder (jQuery “s bind
), og en metode til at offentliggøre en begivenhed til sine abonnenter (trigger
i jQuery). Når du “taler om IO- eller UI-begivenheder, er der også normalt en begivenhedssløjfe , der registrerer nye begivenheder såsom museklik og sender dem til afsenderen. I JS-land, afsenderen og hændelsessløjfen leveres af browseren.
For kode, der interagerer direkte med brugeren – reagerer på tastetryk og klik – begivenhedsdrevet programmering (eller en variation deraf, såsom funktionel reaktiv programmering ) er næsten uundgåelig. Du, programmøren, har ingen idé om, hvornår eller hvor brugeren skal klikke, så det er ned til GUI-rammen eller browser for at registrere brugerens handling i dens hændelsesløkke og underrette din kode. Denne type infrastruktur bruges også i netværksapplikationer (jf. NodeJS).
Dit eksempel, hvor du hæver en begivenhed i yo ur-koden i stedet for at kalde en funktion direkte har nogle mere interessante afvejninger, som jeg vil diskutere nedenfor. Hovedforskellen er, at udgiveren af en begivenhed (makeItSnow
) ikke specificerer modtageren af opkaldet; at “er forbundet andre steder (i opkaldet til bind
i dit eksempel). Dette kaldes brand-og-glem : makeItSnow
annoncerer verden, at det sneer, men det er ligeglad med, hvem der lytter, hvad der sker næste gang, eller når det sker – det udsender simpelthen meddelelsen og støver hænderne af.
Så den begivenhedsstyrede tilgang afkobler afsenderen af meddelelsen fra modtageren. En fordel dette giver dig er, at en given begivenhed kan have flere håndtere. Du kan binde en gritRoads
-funktion til din snehændelse uden at påvirke den eksisterende shovelSnow
-håndterer. Du har fleksibilitet i den måde, din applikation er forbundet på; for at deaktivere en adfærd skal du bare fjerne bind
opkaldet i stedet for at gå på jagt gennem koden for at finde alle forekomsterne af adfærden.
En anden fordel ved begivenhedsdrevet programmering er, at det giver dig et sted at sætte tværgående bekymringer. Begivenhedssenderen spiller rollen som Mediator , og nogle biblioteker (såsom Lysere ) bruger en rørledning, så du nemt kan plug-in generiske krav såsom logning eller servicekvalitet.
Fuld offentliggørelse: Brighter er udviklet på Huddle, hvor jeg arbejder.
En tredje fordel ved at afkoble afsenderen af en begivenhed fra modtageren er, at den giver dig fleksibilitet i når du håndterer begivenheden. Du kan behandle hver type begivenhed på sin egen tråd (hvis din begivenhedssender understøtter den), eller du kan placere hævede begivenheder på en meddelelsesmægler som RabbitMQ og håndtere dem med en asynkron proces eller endda behandle dem i løs vægt natten over. Modtageren af begivenheden kunne være i en separat proces eller på en separat maskine. Du behøver ikke at ændre koden, der hæver begivenheden for at gøre dette! Dette er den store idé bag “mikroservice” -arkitekturer: autonome tjenester kommunikerer ved hjælp af begivenheder med messaging-middelvare som rygraden i applikationen.
For et ret anderledes eksempel på begivenhedsdrevet stil, se på domænestyret design, hvor domænehændelser bruges til at holde aggregater adskilt. Overvej f.eks. En onlinebutik, der anbefaler produkter baseret på din købshistorik. En Customer
skal have sin købshistorik opdateret, når en ShoppingCart
betales for. ShoppingCart
aggregatet underretter muligvis Customer
ved at hæve en CheckoutCompleted
begivenhed; Customer
bliver opdateret i en separat transaktion som reaktion på begivenheden.
Den største ulempe ved denne begivenhedsdrevne model er indirektion. Det er nu sværere at finde koden, der håndterer begivenheden, fordi du ikke bare kan navigere til den ved hjælp af din IDE; du er nødt til at finde ud af, hvor begivenheden er bundet i konfigurationen, og håber, at du har fundet alle håndtererne. Der er flere ting, du kan have i dit hoved til enhver tid. Kodestilkonventioner kan hjælpe her (for eksempel at placere alle opkald til bind
i en fil). Af hensyn til din fornuft er det vigtigt kun at bruge en begivenhedssender og at bruge den konsekvent.
En anden ulempe er, at det er svært at omlægge begivenheder. Hvis du har brug for at ændre formatet på en begivenhed, skal du også ændre alle modtagere. Dette forværres, når abonnenterne på en begivenhed er på forskellige maskiner, for nu skal du synkronisere softwareudgivelser!
Under visse omstændigheder kan ydeevne være et problem. Når en meddelelse behandles, skal afsenderen:
- Slå de rigtige håndterere op i en eller anden datastruktur.
- Byg en meddelelsesbehandlingsrørledning til hver handler. Dette kan involvere en række hukommelsesallokeringer.
- Ring dynamisk til håndtererne (muligvis ved hjælp af refleksion, hvis sproget kræver det).
Dette er bestemt langsommere end en almindelig funktion opkald, som kun indebærer at skubbe en ny ramme på stakken. Imidlertid gør den fleksibilitet, som en begivenhedsdrevet arkitektur giver dig, det meget lettere at isolere og optimere langsom kode. At have evnen til at indsende arbejde til en asynkron processor er en stor gevinst her, da det giver dig mulighed for at tjene en anmodning med det samme, mens det hårde arbejde behandles i baggrunden. Under alle omstændigheder, hvis du interagerer med DBen eller tegner ting på skærmen, vil omkostningerne ved IO fuldstændigt overvinde omkostningerne ved behandling af en besked. Det er et tilfælde af at undgå for tidlig optimering.
Sammenfattende er begivenheder en fantastisk måde at opbygge løst koblet software på, men de er ikke uden omkostninger. Det ville f.eks. Være en fejl at erstatte ethvert funktionsopkald i din applikation med en begivenhed. Brug begivenheder til at skabe meningsfulde arkitektoniske opdelinger.
Kommentarer
- Dette svar siger det samme som 5377 ‘ s svar, som jeg valgte som korrekt; Jeg ‘ ændrer mit valg for at markere dette, fordi det uddybes nærmere.
- Er hastighed en væsentlig ulempe ved begivenhedsdrevet kode? Det ser ud til at det kunne være, men jeg ved ikke ‘.
- @ raptortech97 det kan det bestemt være. For kode, der skal være særlig hurtig, vil du sandsynligvis undgå at sende begivenheder i en indre sløjfe; Heldigvis er det i sådanne situationer som regel veldefineret, hvad du skal gøre, så du behøver ikke ‘ ikke brug for ekstra fleksible begivenheder (eller publicere / abonnere eller observatører, der er ækvivalente mekanismer med forskellig terminologi).
- Bemærk også, at der er nogle sprog (f.eks. Erlang) bygget omkring skuespillermodellen, hvor alt er meddelelser (begivenheder). I dette tilfælde kan kompileren beslutte, om meddelelserne / begivenhederne skal implementeres som direkte funktionsopkald eller som kommunikation.
- For ” performance ” Jeg tror, at vi skal skelne mellem en-tråds ydeevne og skalerbarhed. Beskeder / begivenheder kan være værre for single-threaded ydeevne (men kan konverteres til funktionsopkald til nul ekstra omkostninger og være ikke dårligere), og for skalerbarhed er det ‘ overlegen i stort set alle måde (f.eks. sandsynligvis resultere i massive ydeevne forbedringer på moderne multi-CPU og fremtidige ” mange-CPU ” systemer).
Svar
Begivenhedsbaseret programmering bruges, når programmet ikke styrer rækkefølgen af begivenheder, det udfører. I stedet styres programflowet af en ekstern proces, såsom en bruger (f.eks. GUI), et andet system (f.eks. Klient / server) eller en anden proces (f.eks. RPC).
For eksempel et batchbehandlingsscript ved, hvad det skal gøre, så det gør det bare. Det er ikke hændelsesbaseret.
En tekstbehandler sidder der og venter på, at brugeren begynder at skrive.Tastetryk er begivenheder, der udløser funktionalitet til opdatering af den interne dokumentbuffer. Programmet kan ikke vide, hvad du vil skrive, så det skal være hændelsesdrevet.
De fleste GUI-programmer er hændelsesdrevne, fordi de er bygget op omkring brugerinteraktion. Begivenhedsbaserede programmer er dog ikke begrænset til GUIer, det er simpelthen det mest kendte eksempel for de fleste. Webservere venter på, at klienter opretter forbindelse og følger et lignende udtryk. Baggrundsprocesser på din computer reagerer muligvis også på begivenheder. For eksempel kan en on-demand virusscanner modtage en begivenhed fra operativsystemet vedrørende en nyoprettet eller opdateret fil og derefter scanne filen for vira.
Svar
I en begivenhedsbaseret applikation giver begrebet Event Listers dig mulighed for at skrive endnu mere Løst koblede applikationer.
For eksempel kan et tredjepartsmodul eller plugin slette en post fra databasen og derefter udløse receordDeleted
begivenhed og lad resten til begivenhedslytterne udføre deres arbejde. Alt fungerer fint, selvom udløsermodulet ikke engang ved, hvem der lytter til denne særlige begivenhed, eller hvad der skal ske næste gang.
Svar
En simpel analogi, jeg ville tilføje, der hjalp mig:
Tænk på komponenterne (eller objekterne) i din applikation som en stor gruppe af Facebook-venner.
Når en af dine venner vil fortælle dig noget, kan de enten ringe til dig direkte eller sende det på deres Facebook-væg. Når de sender det til deres Facebook, kan nogen kunne se det og reagere på det, men mange mennesker don t. Nogle gange er det noget vigtigt, at folk sandsynligvis bliver nødt til at reagere på det, som “Vi får en baby!” eller “So-and-so band holder en overraskelseskoncert på Drunkin “Musling bar!”. I det sidste tilfælde vil resten af vennerne sandsynligvis være nødt til at reagere på det, især hvis de er interesserede i dette band.
Hvis din ven ønsker at holde en hemmelighed mellem dig og dem, vil de sandsynligvis ville ikke sende det til deres Facebook-væg, ville de ringe til dig direkte og fortælle dig. Forestil dig et scenarie, hvor du fortæller en pige, du kan lide, at du gerne vil møde hende på en restaurant til en date. I stedet for at ringe til hende direkte og spørge hende, du sender det på din Facebook-væg, så alle dine venner kan se. Dette fungerer, men hvis du har en jaloux eks, kunne hun se det og dukke op på restauranten for at ødelægge din dag.
Hvornår beslutter, om der skal opbygges lyttere til implementering af noget, tænk på denne analogi. Har denne komponent brug for at lægge deres forretning derude, så nogen kan se? Eller har de brug for at ringe til nogen direkte? Ting kan blive rodet ret let, så vær forsigtig.
Svar
Denne følgende analogi kan måske hjælpe dig u for at forstå begivenhedsdrevet I / O-programmering ved at tegne en parallel til ventelinjen ved doktorens reception.
Blokering af I / O er som, hvis du står i køen, beder receptionisten en fyr foran dig om at udfylde formularen, og hun venter, indtil han er færdig. Du bliver nødt til at vente på din tur, indtil fyren er færdig med sin formular, dette blokerer.
Hvis en fyr tager 3 minutter at udfylde, skal den 10. fyr vente til 30 minutter. Nu for at reducere denne 10. fyres ventetid, ville løsningen være at øge antallet af receptionist, hvilket er dyrt. Dette er hvad der sker på traditionelle webservere. Hvis du anmoder om en brugerinfo, skal efterfølgende anmodning fra andre brugere vente til den aktuelle operation, hentning fra databasen, er afsluttet. Dette øger “tid til svar” for den 10. anmodning, og det øges eksponentielt for den n. bruger. For at undgå denne traditionelle webserver oprettes tråd (svarende til stigende antal receptionister) for hver enkelt anmodning det vil sige, dybest set opretter det en kopi af serveren til hver anmodning, som er dyrt interms af CPU-forbrug, da hver anmodning har brug for en operativsystemtråd. For at skalere appen skal du kaste masser af beregningskraft i appen .
Event Driven : Den anden tilgang til skalering af køens “responstid” er at gå til begivenhedsdrevet tilgang, hvor fyr i køen vil blive afleveret formular, bedt om at udfylde og komme tilbage efter udfyldelse. Derfor kan receptionisten altid tage anmodningen. Dette er nøjagtigt, hvad javascript har gjort siden det blev oprettet. I browseren vil javascript svare på brugerhændelsens klikhændelse, rulle, stryge eller hente database og så videre. Dette er muligt i javascript iboende, fordi javascript behandler funktioner som første klasse objekter, og de kan videregives som parametre til andre funktioner (kaldet callbacks) og kan kaldes efter afslutningen af en bestemt opgave. Dette er hvad node.js gør nøjagtigt på serveren.Du kan finde mere info om begivenhedsdrevet programmering og blokering af i / o i forbindelse med node her
$(document).bind('snow', shovelShow)
. Ingen grund til at pakke det ind i en anonym funktion.