hændelsesløkke i node js

Haendelseslokke I Node Js



Node.js er en kraftfuld Javascript-ramme, der gør det muligt for brugere at køre Javascript-kode på serveren uden for browseren. Det er et ikke-blokerende, hændelsesdrevet runtime-miljø til opbygning af pålidelige skalerbare webapplikationer. Hændelsesløkken er en vigtig del af Node.js, der gør det muligt for dig at udføre opgaver uden at vente på, at en er færdig, før du starter en anden.

Selvom Javascript er et enkelt-trådet sprog, kan Node.js tildele opgaver til operativsystemet, så det kan behandle flere opgaver på samme tid. Flere opgaver skal udføres på samme tid, fordi operationer i operativsystemet er multi-threaded. Tilbagekaldet, der er knyttet til hver operation, føjes til hændelseskøen og er planlagt af Node.js til at køre, når den angivne opgave er fuldført.

For at skrive effektiv og pålidelig Node.js-kode skal brugeren have en solid forståelse af hændelsesløkker. Det kan også hjælpe med at fejlfinde ydeevneproblemer effektivt. Hændelsesløkken i Node.js sparer hukommelse og giver dig mulighed for at gøre flere ting på én gang uden at skulle vente på, at hver enkelt er færdig. Udtrykket 'asynkron' refererer til enhver Javascript-funktion, der kører i baggrunden uden at blokere indgående anmodninger.







Før vi hopper direkte til begivenhedsløkker, lad os se på forskellige aspekter af Javascript-programmeringssproget.



Javascript som et asynkront programmeringssprog

Lad os se på koncepterne for asynkron programmering. Javascript bruges i web-, mobil- og desktopapplikationer, men det skal bemærkes, at Javascript er et enkelt-trådet, synkront computerprogrammeringssprog.



Et simpelt kodeeksempel er givet for at forstå konceptet.





funktionsmetode 1 ( ) {

konsol. log ( 'Funktion 1' )

}

funktionsmetode 2 ( ) {

konsol. log ( 'Funktion 2' )

}

metode 1 ( )

metode 2 ( )

I denne kode oprettes to simple funktioner, og metode1 kaldes først, så den logger metode1 først og derefter flyttes til den næste.

Produktion



Javascript som et synkront programmeringssprog

Javascript er et synkront programmeringssprog og udfører hver linje trin for trin ved at bevæge sig fra top til bund med kun én linje udført ad gangen. I eksemplet ovenfor logges metode1 først i terminalen og derefter metode2.

Javascript som blokeringssprog

At være et synkront sprog har javascript en blokerende funktionalitet. Det er ligegyldigt, hvor lang tid det tager at fuldføre en igangværende proces, men en ny proces vil ikke blive startet, før den forrige er afsluttet. I ovenstående kodeeksempel antag, at der er en masse kodescript i metode1, uanset hvor lang tid det tager, enten 10 sekunder eller et minuts metode2 vil ikke blive udført, før al koden i metode1 er blevet eksekveret.

Brugere har muligvis oplevet dette under browsing. Når en webapplikation køres i en browser i back-end, bliver der afviklet en stor del af kode, så browseren ser ud til at være frosset i nogen tid, før kontroladgangen returneres til brugeren. Denne adfærd er kendt som blokering. Browseren kan ikke modtage yderligere indkommende anmodninger, før den aktuelle anmodning er blevet behandlet.

Javascript er et enkelt-trådet sprog

For at køre et program i javascript bruges trådfunktionaliteten. Tråde er kun i stand til at udføre én opgave ad gangen. Andre programmeringssprog understøtter multi-threading og kan køre flere opgaver parallelt, javascript indeholder kun én tråd til at udføre et kodescript.

Venter i Javascript

Som det fremgår af navnet i dette afsnit, er vi nødt til at vente på, at vores anmodning behandles for at fortsætte. Ventetiden kan tage flere minutter, hvor der ikke modtages yderligere anmodninger. Hvis kodescriptet fortsætter uden at vente, vil koden støde på en fejl. Noget funktionalitet skal implementeres i Javascript eller mere specifikt Node.js for at gøre koden asynkron.

Nu hvor vi har forstået de forskellige aspekter af Javascript, lad os forstå synkron og asynkron ved nogle simple eksempler.

Synkron eksekvering af kode i Javascript

Synkron betyder, at koden udføres sekventielt eller mere simpelt trin-for-trin startende fra toppen og bevæger sig nedad linje for linje.

Nedenfor er givet et eksempel, som kan hjælpe med at forstå:

// application.js

konsol. log ( 'En' )

konsol. log ( 'To' )

konsol. log ( 'Tre' )

I denne kode er der tre console.log-sætninger, der hver udskriver noget. For det første sendes den første erklæring, som skal udskrive 'En' i konsollen, ind i opkaldsstakken i 1 ms (estimeret), derefter logges den til terminalen. Derefter skubbes den anden sætning ind i opkaldsstakken, og nu er tiden 2 ms med en tilføjet fra den forrige, og så logger den 'To' til konsollen. Til sidst skubbes den sidste sætning ind i opkaldsstakken, for nu er tiden 3ms, og den logger 'Tre' i konsollen.

Ovenstående kode kan udføres ved at påkalde følgende kommando:

node applikation. js

Produktion

Funktionen er forklaret i detaljer ovenfor, og ved at tage den i betragtning logges outputtet på konsollen på et øjeblik:

Asynkron udførelse af kode i Javascript

Lad os nu omstrukturere den samme kode ved at introducere tilbagekald og gøre koden asynkron. Ovenstående kode kan omdannes som:

// application.js
funktion printOne ( ring tilbage ) {
sætTimeout ( fungere ( ) {
konsol. log ( 'En' ) ;
ring tilbage ( ) ;
} , 1000 ) ;
}
funktion printTo ( ring tilbage ) {
sætTimeout ( fungere ( ) {
konsol. log ( 'To' ) ;
ring tilbage ( ) ;
} , 2000 ) ;
}
funktion printThree ( ) {
sætTimeout ( fungere ( ) {
konsol. log ( 'Tre' ) ;
} , 3000 ) ;
}
konsol. log ( 'Start af programmet' ) ;
printOne ( fungere ( ) {
printTo ( fungere ( ) {
printThree ( ) ;
} ) ;
} ) ;
konsol. log ( 'Slut på programmet' ) ;

I denne kode ovenfor:

  • Tre funktioner erklæres til at udskrive 'En', 'To' og 'Tre', hver funktion har en tilbagekaldsparameter, der tillader sekventiel eksekvering af kode.
  • En timeout indstilles ved hjælp af setTimeout-funktionen, og der er en console.log-sætning til udskrivning efter en specifik forsinkelse.
  • To meddelelser udskrives 'Start af programmet' og 'Slut på programmet', som angiver begyndelsen og slutningen af ​​programmet.
  • Programmet starter med at udskrive “Start of the Program” hvorefter printOne-funktionen udføres med 1 sekunds forsinkelse, derefter printTwo udføres med 2 sekunders forsinkelse, og til sidst udføres printThree-funktionen med 3 sekunders forsinkelse forsinke.
  • Programmet venter ikke på de asynkrone kodeudførelser inde i setTimeouts-funktionerne, som logger 'End of the Program'-erklæringen, før de udskriver One, Two og Three.

Produktion

Kør ovenstående kode ved at udføre denne kommando i terminalen:

node applikation. js

Nu vil udgangen i terminalen dukke op asynkront som:

Nu hvor vi har en fuldstændig forståelse af den synkrone og asynkrone udførelse, lad os springe for at styrke vores koncept for begivenhedsløkke i Node.js.

Node.js: Event Loop Mechanism

Udførelsen af ​​både synkrone og asynkrone opgaver styres af hændelsesløkken i Node.js. Eksekveringen påkaldes, så snart Node.js-projektet er lanceret og overfører problemfrit de komplekse opgaver til systemet. Dette sikrer, at andre opgaver kan køre problemfrit på hovedtråden.

Visuel forklaring af Event Loop i Node.js

Hændelsesløkken er kontinuerlig og semi-uendelig i Node.js. Hændelsesløkken påkaldes af starten af ​​Node.js-kodescriptet, og den er ansvarlig for at foretage asynkrone API-kald og kalde processer.Tick(), og planlægningstimere genoptager derefter udførelsen af ​​hændelsesløkken.

I Node.js håndterer fem hovedtyper af køer tilbagekald:

  • 'Timer-køen', almindeligvis kendt som en min-heap, er ansvarlig for at håndtere tilbagekald forbundet med 'setTimeout' og 'setInterval'.
  • Tilbagekaldene for asynkrone operationer som i 'fs' og 'http' moduler håndteres af 'I/O Queue'.
  • 'Check Queue' indeholder tilbagekald for funktionen 'setImmediate', som er unik for Node.
  • 'Luk køen' administrerer tilbagekald forbundet med enhver asynkron opgaves lukningsbegivenhed.
  • Endelig er der to forskellige køer i 'Micro Task'-køen:
    • 'nextTick'-køen indeholder tilbagekald forbundet med 'process.nextTick'-funktionen.
    • 'Promise'-køen styrer tilbagekald relateret til native Promise.

Event Loop-funktionalitet i Node.js

Hændelsesløkken fungerer under specifikke krav, der styrer tilbagekaldsudførelsesordren. Brugerens synkrone Javascript-kode prioriteres i starten af ​​processen, så hændelsesløkken starter først, når opkaldsstakken er ryddet. Følgende udførelsessekvens følger et struktureret mønster:

Den højeste prioritet gives til tilbagekaldene i mikrotask-køen og derefter flytte til at udføre opgaverne i den næsteTick-kø efterfulgt af opgaverne i løftekøen. Processerne i timerens kø-tilbagekald håndteres derefter, hvorefter mikrotask-køen besøges igen efter hvert timer-tilbagekald. Tilbagekaldene i I/O-, tjek- og lukkøerne udføres derefter i et lignende mønster med mikrotask-køen besøgt efter hver fase.

Sløjfen fortsætter med at udføre, hvis der er flere tilbagekald at behandle. Når kodescriptet er afsluttet, eller der ikke er tilbagekald tilbage at behandle, slutter hændelsesløkken effektivt.

Nu hvor vi dybt forstår Event-løkken, lad os se på dens funktioner.

Funktioner af event loop i Node.js

De vigtigste funktioner er:

  • Hændelsesløkken er en uendelig løkke og fortsætter med at udføre opgaverne, så snart den modtager dem og går i dvaletilstand, hvis der ikke er nogen opgaver, men begynder at fungere, så snart opgaven er modtaget.
  • Opgaverne i begivenhedskøen udføres kun, når stakken er tom, hvilket betyder, at der ikke er nogen aktiv handling.
  • Tilbagekald og løfter kan bruges i event-loopet.
  • Da begivenhedsløkken følger princippet om abstrakt datatypekø, opfylder den den første opgave og fortsætter derefter til den næste.

Efter en grundig forståelse af hændelsesløjfen og logikken i asynkrone og synkrone eksekveringer, kan det at få en forståelse af de forskellige faser størkne begreberne i hændelsesløjfen.

Node.js Event loop Phases

Som nævnt ovenfor er begivenhedsløkken semi-uendelig. Det har mange faser, men nogle faser bruges til intern håndtering. Disse faser har ingen effekt på kodescriptet.

Hændelsesløkken følger funktionaliteten af ​​Queue og udfører opgaven efter princippet om først ind og først ud. De planlagte timere vil blive håndteret af operativsystemet, indtil de udløber. De udløbne timere føjes derefter til tilbagekaldskøen for timere.

Hændelsesløkken udfører opgaverne i timerens kø én efter én, indtil der ikke er flere opgaver tilbage, eller den når det maksimalt tilladte antal opgaver. I sektionerne nedenfor forklares kernefaserne af begivenhedsløkker.

Timers fase

I Node.js er der en timer API, der kan planlægge de funktioner, der skal udføres i fremtiden. Når den tildelte tid er gået, vil timeren tilbagekald udføres, så snart de kan planlægges; dog kan en forsinkelse forekomme enten fra operativsystemets ende eller på grund af udførelse af andre tilbagekald.

Timers API har tre hovedfunktioner:

  • sætTimeout
  • indstilles øjeblikkeligt
  • sætinterval

De ovennævnte funktioner er synkrone. Timerfasen i hændelsesløkken har sit omfang begrænset til funktionerne setTimeout og setInterval. Mens check-funktionen håndterer setImmediate-funktionen.

Lad os overveje et simpelt eksempel for at styrke den teoretiske del:

// application.js

funktion forsinket Funktion ( ) {

konsol. log ( 'den forsinkede funktion udføres efter timeout' ) ;

}

konsol. log ( 'Start af programmet' ) ;

sætTimeout ( forsinket funktion, 2000 ) ;

konsol. log ( 'Slut på programmet' ) ;

I denne kode:

  • Programmet starter med at logge erklæringen 'Start af programmet' til terminalen.
  • Derefter kaldes delayedFunction med en timer på 2ms, kodescriptet stopper ikke og fortsætter med at håndtere forsinkelsen i baggrunden.
  • Udsagnet 'Programmets afslutning logges efter den første erklæring.
  • Efter en forsinkelse på 2ms logges sætningen i delayedFunction til terminalen.

Produktion

Outputtet vises som:

Det kan ses, at koden ikke er stoppet for at den delayedFunction kan behandle; den bevæger sig fremad, og efter forsinkelsen behandles tilbagekaldet af funktionen.

Afventende tilbagekald

Hændelsesløkken tjekker for de hændelser, der sker, såsom læsning af filer, netværksaktiviteter eller input/output-opgaver, i pollingfasen. Det er vigtigt at vide, at det i Node.js kun er nogle af begivenhederne, der håndteres i denne afstemningsfase. I den efterfølgende iteration af hændelsesløkken kan visse hændelser dog blive udskudt til den afventende fase. Dette er et nøglekoncept at huske på, når du optimerer og fejlfinder Node.js-kode, der involverer komplekse hændelsesdrevne operationer.

Det er vigtigt at forstå, at under den ventende tilbagekaldsfase tilføjer begivenhedsløkken udskudte begivenheder til køen af ​​afventende tilbagekald og udfører dem. Denne fase håndterer også nogle TCP socket fejl, som systemet har genereret, såsom ECONNREFUSED fejlhændelser på visse operativsystemer.

Nedenfor nævnes et eksempel for at styrke konceptet:

// application.js
konst fs = kræve ( 'fs' ) ;
funktion readFileAsync ( filsti, tilbagekald ) {
fs. læs fil ( './PromiseText.txt' , 'utf8' , funktion ( fejl, data ) {
hvis ( fejl ) {
konsol. fejl ( ` Fejl læse fil : $ { fejl. besked } ` ) ;
} andet {
konsol. log ( ` Fil indhold : $ { data } ` ) ;
}
ring tilbage ( ) ;
} ) ;
}
konsol. log ( 'Start af programmet' ) ;
readFileAsync ( './PromiseText.txt' , funktion ( ) {
konsol. log ( 'Fil læst tilbagekald udført' ) ;
} ) ;
konsol. log ( 'Slut på programmet' ) ;

I denne kode:

  • Programmet startes ved at logge sætningen 'Start af programmet' i terminalen.
  • ReadFileAsync er defineret asynkront til at læse indholdet af filen 'PromiseText.txt'. Det er en parametriseret funktion, der udfører en tilbagekaldsfunktion, efter at filen er blevet læst.
  • ReadFileAsync-funktionen kaldes for at starte processen med fillæsning.
  • I processen med fillæsning stopper programmet ikke; i stedet fortsætter den til den næste sætning og logger den ind i terminalen 'Programslut'.
  • Den asynkrone hændelse af fillæsning behandles i baggrunden af ​​hændelsesløkken.
  • Efter at filen er blevet læst asynkront, og indholdet er blevet logget til terminalen, logger programmet filindholdet til terminalen. Derefter logger den følgende besked 'File læst tilbagekald udført'.
  • Hændelsesløkken håndterer de afventende tilbagekaldsoperationer i næste fase.

Produktion

Resultatet af ovenstående udførelse er:

Inaktiv, Forbered fase i Node.js

Den inaktive fase bruges til at håndtere interne funktioner i Node.js, så det er ikke en standardfase. Det påvirker ikke kodescriptet. Den inaktive fase er som en pauseperiode for hændelsessløjfen, hvor du håndterer de lavprioriterede opgaver i baggrunden. Et simpelt eksempel til at forstå denne fase er:

konst { ledig } = kræve ( 'tom-gc' ) ;

ledig. ignorere ( ) ;

I denne kode bruges 'idle-gc' modulet, der gør det muligt at ignorere tomgangsfasen. Dette tjener til at håndtere situationer, hvor begivenhedsløkken er optaget, og baggrundsopgaver ikke udføres. Brugen af ​​idle.ignore anses ikke for at være optimal, da det kan forårsage præstationsproblemer.

Afstemningsfase i Node.js

Afstemningsfasen i Node.js fungerer som:

  • Den håndterer begivenhederne i afstemningskøen og udfører deres tilsvarende opgaver.
  • Det bestemmer, hvor meget tid der skal bruges på at vente og tjekke for I/O-operationerne i processen.

Når hændelsesløkken går ind i afstemningsfasen på grund af fraværet af en timer, udføres en af ​​nedenstående opgaver:

  • I afstemningsfasen af ​​hændelsesløkken i Node.js sættes de afventende I/O-hændelser i kø og udføres derefter i en sekventiel procedure efter princippet om først ind og først ud, indtil køen bliver tom. Under udførelsen af ​​callbacks er næsteTick- og mikrotask-køerne også i aktion. Dette sikrer glathed og gør det muligt at håndtere I/O-operationer mere effektivt og pålideligt.
  • Hvis køen er tom, og scriptet ikke er blevet planlagt af setImmediate()-funktionen, slutter hændelsesløkken, og den vil fortsætte til næste fase(check). På den anden side, hvis script-planlægningen er blevet udført af setImmediate()-funktionen, tillader hændelsesløkken, at tilbagekaldene tilføjes til køen, som vil blive udført af den.

Dette illustreres bedst med et simpelt kodeeksempel:

sætTimeout ( ( ) => {

konsol. log ( 'Asynkronisering er fuldført' ) ;

} , 2000 ) ;

konsol. log ( 'Start' ) ;

indstilles øjeblikkeligt ( ( ) => {

konsol. log ( 'setImmediate callback udført' ) ;

} ) ;

konsol. log ( 'Ende' ) ;

I denne kode:

  • To meddelelser 'Start' og 'End' angiver starten og afslutningen af ​​programmet.
  • Funktionen setTimeout() indstiller en tilbagekaldsfunktion med en forsinkelse på 2 ms og logger 'Async operation completed' til terminalen.
  • Funktionen setImmediate() logger meddelelsen 'setImmediate callback executed' til terminalen, efter at Start-meddelelsen er blevet logget til terminalen.

Produktion

Outputtet ville vise meddelelserne med blot et minuts observation, at 'Async operation completed' tager tid og udskrives efter 'End'-meddelelsen:

Node.js Check Phase

Efter afstemningsfasen er udført, udføres tilbagekaldene i kontrolfasen. Hvis et kodescript er planlagt ved hjælp af setImmediate()-funktionen, og poll-funktionen er gratis, fungerer hændelsesløkken ved at gå direkte til kontrolfasen i stedet for at forblive inaktiv. Funktionen setImmediate() er en unik timer, der fungerer under de forskellige faser af hændelsesløkken.

libuv API'en bruges til at planlægge callback-udførelserne efter afstemningsfasen er afsluttet. Under kodeudførelsen går begivenhedsløkken ind i afstemningsfasen, hvor den venter på de indkommende forbindelsesanmodninger. I et andet tilfælde, hvis tilbagekaldet er planlagt ved hjælp af setImmediate()-funktionen, og afstemningsfasen afsluttes uden aktivitet, vil den gå til kontrolfasen i stedet for at vente. Overvej nedenstående eksempel for at forstå:

// application.js

konsol. log ( 'Start' ) ;

indstilles øjeblikkeligt ( ( ) => {

konsol. log ( 'Øjeblikkelig tilbagekald' ) ;

} ) ;

konsol. log ( 'Ende' ) ;

I denne kode er tre beskeder logget på terminalen. SetImmediate()-funktionen sender så endelig et tilbagekald for at logge beskeden ' Omgående tilbagekald ” til terminalen.

Produktion

Outputtet af ovenstående kode vil dukke op i følgende rækkefølge:

Node.js lukker tilbagekald

Node.js bruger denne lukkefase til at køre tilbagekald for at lukke hændelser og afslutte en hændelsesløkkeiteration. Efter at forbindelsen er lukket, håndterer hændelsessløjfen de afsluttende hændelser i denne fase. I denne fase af hændelsesløkken genereres og behandles 'nextTick()' og mikroopgaver på samme måde som andre faser.

Process.exit-funktionen bruges til at afslutte hændelsesløkken på et hvilket som helst tidspunkt. Hændelsesløkken vil se bort fra eventuelle afventende asynkrone operationer, og Node.js-processen afsluttes.

Et simpelt eksempel at overveje er:

// application.js
konst net = kræve ( 'net' ) ;
konst server = net. opretteServer ( ( stikkontakt ) => {
stikkontakt. ( 'tæt' , ( ) => {
konsol. log ( 'Stikkontakt lukket' ) ;
} ) ;
stikkontakt. ( 'data' , ( data ) => {
konsol. log ( 'Modtaget data:' , data. til String ( ) ) ;
} ) ;
} ) ;
server. ( 'tæt' , ( ) => {
konsol. log ( 'Server lukket' ) ;
} ) ;
konst Havn = 3000 ;
server. Hør efter ( Havn, ( ) => {
konsol. log ( `Server lytter på port $ { Havn } ` ) ;
} ) ;
sætTimeout ( ( ) => {
konsol. log ( 'Lukker server efter 10 sekunder' ) ;
server. tæt ( ) ;
behandle. Afslut ( ) ;
} , 10.000 ) ;

I denne kode:

  • const net = kræver('net') ' importerer det netmodul, der kræves til at håndtere en TCP-server og ' const server = net.createServer((socket) => { ” opretter en ny TCP-serverinstans.
  • socket.on('luk', () => {... } ” lytter til “tæt” på alle stikkontakter. Når stikkontakten er lukket, logges meddelelsen 'Socket lukket' til terminalen.
  • socket.on('data', (data) => {} ” kontrollerer for indgående data fra alle de individuelle sockets og udskriver dem ved hjælp af “.toString()”-funktionen.
  • server.on('luk', () => {...} ” tjekker for “close”-hændelsen på selve serveren, og når serverforbindelsen er lukket logger den “Server Closed”-meddelelsen til terminalen.
  • server.listen(port, () => {…} ” lytter til indgående forbindelser på porten.
  • setTimeout(() => {…} ” indstiller en timer på 10 ms for at lukke serveren.

Det afslutter diskussionen om de forskellige faser af event-loopet i Node.js. Før vi hopper til en konklusion, lad os diskutere en sidste ting, nemlig hvordan man forlader begivenhedsløkken i Node.js.

Afslutning af hændelsesløkken i Node.js

Hændelsesløkken er i udførelsesfasen, så længe der er nogle opgaver i alle køer af hændelsesløkkefaser. Hændelsesløkken slutter, efter at exitfasen er udsendt, og exit-lytterens tilbagekald vender tilbage, hvis der ikke er flere opgaver i køerne.

Den eksplicitte måde at afslutte en hændelsesløkke på er at bruge '.exit'-metoden. De aktive processer i Node.js vil afslutte øjeblikkeligt, så snart process.exit-funktionen kaldes. Alle planlagte og afventende begivenheder vil blive slettet:

behandle. ( 'Afslut' , ( kode ) => {

konsol. log ( `Forlader med udgangskode : $ { kode } ` ) ;

} ) ;

behandle. Afslut ( 1 ) ;

Brugere kan lytte til .exit-funktionen. Det skal bemærkes, at '.exit'-funktionen skal være synkron, da Node.js-programmet afsluttes, så snart det lytter til denne begivenhed.

Dette afslutter diskussionen om event-loopet. En dybdegående artikel, der har dækket alle koncepter, faser og eksempler relateret til event-loopet.

Konklusion

Før du forstår hændelsesløkken, kan en oversigt over de synkrone og asynkrone koncepter hjælpe med at forstå kodeflowet i hændelsesløkken. Synkron eksekvering betyder trin-for-trin eksekvering, mens asynkron eksekvering betyder at stoppe nogle trin uden at vente på deres færdiggørelse. Begivenhedsløkkens virkemåde sammen med alle faser sammen med passende eksempler diskuteres i artiklen.