Jag har skickat återuppringningar eller bara utlöst funktionerna från andra funktioner i mina program för att få saker att hända när uppgifterna är klara. När något är klart aktiverar jag funktionen direkt:
var ground = "clean"; function shovelSnow(){ console.log("Cleaning Snow"); ground = "clean"; } function makeItSnow(){ console.log("It"s snowing"); ground = "snowy"; shovelSnow(); }
Men jag har läsa om många olika strategier i programmering, och en som jag förstår är kraftfull men som jag ännu inte har praktiserat, är händelsebaserad (jag tror att en metod jag läste om hette ”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);
Jag vill förstå de objektiva styrkorna och svagheterna i händelsen -baserad programmering, vs bara att ringa alla dina funktioner från andra funktioner. I vilka programmeringssituationer är händelsebaserad programmering vettigt att använda?
Kommentarer
Svar
En händelse är ett meddelande som beskriver en händelse från det senaste förflutna.
En typisk implementering av ett händelsestyrt system använder en händelsedispenser och hanterarfunktioner (eller prenumeranter ). Avsändaren tillhandahåller ett API för att leda hanterare till händelser (jQuery ”s bind
), och en metod för att publicera en händelse till sina abonnenter (trigger
i jQuery). När du ”talar om IO- eller UI-händelser, finns det vanligtvis också en händelsesslinga som upptäcker nya händelser som musklick och skickar dem till avsändaren. JS-land, avsändaren och händelsesslingan tillhandahålls av webbläsaren.
För kod som interagerar direkt med användaren – svarar på tangenttryckningar och klick – händelsestyrd programmering (eller en variation därav, såsom funktionell reaktiv programmering ) är nästan oundviklig. Du, programmeraren, har ingen aning om när eller var användaren ska klicka, så det är ner till GUI-ramverket eller webbläsare för att upptäcka användarens åtgärd i händelsesslingan och meddela din kod. Denna typ av infrastruktur används också i nätverksapplikationer (se NodeJS).
Ditt exempel, där du tar upp en händelse i yo ur-koden istället för att anropa en funktion direkt har några mer intressanta avvägningar, vilket jag kommer att diskutera nedan. Huvudskillnaden är att utgivaren av en händelse (makeItSnow
) inte anger mottagaren för samtalet; som ”har kopplats upp någon annanstans (i samtalet till bind
i ditt exempel). Detta kallas eld-och-glöm : makeItSnow
meddelar världen att det snöar, men det bryr sig inte vem som lyssnar, vad som händer nästa eller när det händer – det sänder helt enkelt meddelandet och dammar av händerna.
Så händelsestyrd metod frikopplar avsändaren av meddelandet från mottagaren. En fördel som detta ger dig är att en given händelse kan ha flera hanterare. Du kan binda en gritRoads
-funktion till din snöhändelse utan att påverka den befintliga shovelSnow
-hanteraren. Du har flexibilitet i hur din applikation kopplas ihop; för att stänga av ett beteende behöver du bara ta bort bind
snarare än att gå på jakt genom koden för att hitta alla förekomster av beteendet.
En annan fördel med händelsestyrd programmering är att den ger dig någonstans att ställa tvärgående problem. Eventutdelaren spelar rollen som Mediator , och vissa bibliotek (som Ljusare ) använder en pipeline så att du enkelt kan plugga in generiska krav som loggning eller servicekvalitet.
Fullständig information: Brighter är utvecklad på Huddle, där jag arbetar.
En tredje fördel med att koppla bort avsändaren av en händelse från mottagaren är att det ger dig flexibilitet i när du hanterar händelsen. Du kan bearbeta varje typ av händelse på sin egen tråd (om din evenemangssändare stöder det), eller så kan du lägga upp händelser på en meddelandemäklare som RabbitMQ och hantera dem med en asynkron process eller till och med bearbeta dem i bulk över natten. Mottagaren av evenemanget kan vara i en separat process eller på en separat maskin. Du behöver inte ändra koden som höjer händelsen för att göra detta! Detta är den stora idén bakom ”microservice” -arkitekturer: autonoma tjänster kommunicerar med hjälp av händelser, med messaging-mellanprogram som applikationens ryggrad.
För ett ganska annorlunda exempel på händelsestyrd stil, se till domänstyrd design, där domänhändelser används för att hålla aggregat åtskilda. Tänk till exempel på en webbutik som rekommenderar produkter baserat på din köphistorik. En Customer
måste ha sin inköpshistorik uppdaterad när en ShoppingCart
betalas. ShoppingCart
aggregatet kan meddela Customer
genom att höja en CheckoutCompleted
händelse; Customer
skulle uppdateras i en separat transaktion som svar på händelsen.
Huvudnackdelen med den här händelsestyrda modellen är inriktning. Det är nu svårare att hitta koden som hanterar händelsen eftersom du inte bara kan navigera till den med din IDE; du måste ta reda på var händelsen är bunden i konfigurationen och hoppas att du har hittat alla hanterare. Det finns fler saker att hålla i ditt huvud när som helst. Kodstilkonventioner kan hjälpa till här (till exempel placera alla samtal till bind
i en fil). För din förnuft är det viktigt att bara använda en händelsedispenser och att använda den konsekvent.
En annan nackdel är att det är svårt att refaktorera händelser. Om du behöver ändra formatet på en händelse måste du också ändra alla mottagare. Detta förvärras när prenumeranterna på en händelse är på olika maskiner, för nu måste du synkronisera programversioner!
Under vissa omständigheter kan prestanda vara ett problem. Vid bearbetning av ett meddelande måste avsändaren:
- Leta upp rätt hanterare i någon datastruktur.
- Bygg en pipeline för meddelandehantering för varje hanterare. Detta kan innebära en massa minnestilldelningar.
- Ring dynamiskt handterarna (möjligen med reflektion om språket kräver det).
Detta är verkligen långsammare än en vanlig funktion samtal, vilket bara innebär att man trycker på en ny ram på stacken. Men flexibiliteten som en händelsestyrd arkitektur ger dig gör det mycket lättare att isolera och optimera långsam kod. Att ha förmågan att skicka arbete till en asynkron processor är en stor vinst här, eftersom det gör att du kan betjäna en begäran omedelbart medan det hårda arbetet behandlas i bakgrunden. I vilket fall som helst, om du interagerar med DB eller ritar saker på skärmen, kommer kostnaderna för IO att helt överföra kostnaderna för att behandla ett meddelande. Det är ett fall att undvika för tidig optimering.
Sammanfattningsvis är händelser ett utmärkt sätt att bygga löst kopplad programvara, men de är inte utan kostnad. Det skulle till exempel vara ett misstag att ersätta varje funktionsanrop i din applikation med en händelse. Använd händelser för att göra meningsfulla arkitektoniska uppdelningar.
Kommentarer
- Detta svar säger samma sak som 5377 ’ s svar som jag valde som korrekt; Jag ’ ändrar mitt val för att markera det här eftersom det vidareutvecklas.
- Är hastighet en betydande nackdel med händelsestyrd kod? Det verkar som om det kan vara, men jag vet inte ’.
- @ raptortech97 det kan det verkligen vara. För kod som behöver vara särskilt snabb skulle du förmodligen vilja undvika att skicka händelser i en inre slinga. Lyckligtvis är det i sådana situationer vanligtvis väldefinierat vad du behöver göra, så du behöver inte ’ extra flexibelt av händelser (eller publicera / prenumerera eller observatörer, vilket är likvärdiga mekanismer med olika terminologi).
- Observera också att det finns vissa språk (t.ex. Erlang) byggda kring skådespelarmodellen där allt är meddelanden (händelser). I det här fallet kan kompilatorn bestämma om meddelanden / händelser ska implementeras som direkta funktionssamtal eller som kommunikation.
- För ” prestanda ” Jag tror att vi måste skilja mellan entrådig prestanda och skalbarhet. Meddelanden / händelser kan vara värre för entrådig prestanda (men kan omvandlas till funktionsanrop för noll extra kostnad och inte vara sämre), och för skalbarhet ’ är överlägsen i praktiskt taget alla sätt (t.ex. sannolikt att resultera i massiva prestandaförbättringar på modern multi-CPU och framtida ” många-CPU ” -system).
Svar
Händelsebaserad programmering används när programmet inte styr den sekvens av händelser som det utför. I stället styrs programflödet av en extern process som en användare (t.ex. GUI), ett annat system (t.ex. klient / server) eller en annan process (t.ex. RPC).
Till exempel ett batchbehandlingsskript vet vad den behöver göra så det gör det bara. Det är inte händelsebaserat.
En ordbehandlare sitter där och väntar på att användaren ska börja skriva.Nyckeltryck är händelser som utlöser funktionalitet för att uppdatera den interna dokumentbufferten. Programmet kan inte veta vad du vill skriva, så det måste vara händelsestyrt.
De flesta GUI-program är händelsestyrda eftersom de bygger på användarinteraktion. Händelsebaserade program är dock inte begränsade till GUI, det är helt enkelt det mest kända exemplet för de flesta. Webbservrar väntar på att klienter ska ansluta och följa ett liknande uttryck. Bakgrundsprocesser på din dator kan också svara på händelser. Exempelvis kan en on-demand-virusskanner ta emot en händelse från operativsystemet angående en nyskapad eller uppdaterad fil och sedan skanna filen efter virus.
Svar
I en händelsebaserad applikation ger begreppet Event Listers dig möjlighet att skriva ännu mer Löst kopplade applikationer.
Till exempel kan en tredjepartsmodul eller plugin ta bort en post från databasen och sedan utlösa receordDeleted
händelse och lämna resten till evenemangets lyssnare att göra sitt jobb. Allt kommer att fungera bra, även om utlösningsmodulen inte ens vet vem som lyssnar på just den här händelsen eller vad som ska hända härnäst.
Svar
En enkel analogi som jag ville lägga till som hjälpte mig:
Tänk på komponenterna (eller objekten) i din applikation som en stor grupp Facebook-vänner.
När en av dina vänner vill berätta något för dig kan de antingen ringa dig direkt eller lägga upp det på sin Facebook-vägg. När de lägger upp det på sin Facebook kan någon kunde se det och reagera på det, men många människor gör det inte. Ibland är det något viktigt att människor sannolikt kommer att behöva reagera på det, som ”Vi” får en bebis! ”eller” So-and-so band gör en överraskningskonsert på Drunkin ”Clam bar!”. I det sista fallet kommer resten av vännerna sannolikt att behöva reagera på det, speciellt om de är intresserade av det bandet.
Om din vän vill hålla en hemlighet mellan dig och dem, troligen de skulle inte lägga upp det på sin Facebook-vägg, de skulle ringa dig direkt och berätta. Tänk dig ett scenario där du säger till en tjej du gillar att du vill träffa henne på en restaurang för ett datum. Istället för att ringa henne direkt och fråga henne, du lägger upp den på din Facebook-vägg så att alla dina vänner kan se. Det här fungerar, men om du har ett svartsjukt ex, kan hon se det och dyka upp på restaurangen för att förstöra din dag.
När besluta om huruvida lyssnare ska byggas eller inte för att implementera något, tänk på denna analogi. Behöver den här komponenten sätta sin verksamhet där ute för att någon ska se? Eller behöver de ringa någon direkt? Saker kan bli rörigt ganska lätt så var försiktig.
Svar
Denna följande analogi kan hjälpa dig u för att förstå händelsedriven I / O-programmering genom att rita en parallell till väntelinjen vid doktors reception.
Att blockera I / O är som om du står i kön, frågar receptionisten en kille framför dig att fylla i formuläret och hon väntar tills han är klar. Du måste vänta på din tur tills killen är klar med formuläret, det här blockeras.
Om en kille tar 3 minuter att fylla i, måste den 10: e killen vänta till 30 minuter. Nu för att minska den här 10: e killens väntetid, skulle lösningen vara att öka antalet receptionister, vilket är dyrt. Detta är vad som händer på traditionella webbservrar. Om du begär en användarinformation bör efterföljande begäran från andra användare vänta tills nuvarande operation, hämtning från databas, är slutförd. Detta ökar ”tid till svar” för den 10: e begäran och den ökar exponentiellt för nionde användare. För att undvika denna traditionella webbservrar skapas tråd (motsvarande ökande antal receptionister) för varje enskild begäran , det vill säga, det skapar i princip en kopia av servern för varje förfrågan som är kostsam interms av CPU-förbrukningen eftersom varje förfrågan kommer att behöva en operativsystemtråd. För att skala upp appen måste du kasta mycket beräkningskraft i appen .
Händelsestyrd : Den andra metoden för att skala upp köns ”svarstid” är att gå till evenemangsdrivet tillvägagångssätt, där killen i kön kommer att överlämnas till formulär, ombedd att fylla i och komma tillbaka när det är klart. Därför kan receptionisten alltid ta begäran. Detta är exakt vad javascript har gjort sedan starten. I webbläsaren svarar javascript på användarklickhändelse, bläddrar, sveper eller hämtar databas och så vidare. Detta är möjligt i javascript i sig, eftersom javascript behandlar funktioner som första klass objekt och de kan skickas som parametrar till andra funktioner (kallas återuppringningar) och kan anropas när en viss uppgift är klar. Det är vad exakt node.js gör på servern.Du kan hitta mer info om händelsestyrd programmering och blockering av i / o, i samband med nod här
$(document).bind('snow', shovelShow)
. Du behöver inte slå in den i en anonym funktion.