JavaScripts utførelsesmodell er nyansert og lett å misforstå. Å lære om hendelsessløyfen i kjernen kan hjelpe.

JavaScript er et enkelt-tråds språk, bygget for å håndtere oppgaver én om gangen. Imidlertid lar hendelsesløkken JavaScript håndtere hendelser og tilbakeringinger asynkront ved å emulere samtidige programmeringssystemer. Dette sikrer ytelsen til JavaScript-applikasjonene dine.

Hva er JavaScript Event Loop?

JavaScripts hendelsesløkke er en mekanisme som kjører i bakgrunnen til hver JavaScript-applikasjon. Den lar JavaScript håndtere oppgaver i rekkefølge uten å blokkere hovedtråden for utførelse. Dette omtales som asynkron programmering.

Hendelsesløkken holder en kø med oppgaver som skal kjøres og mater disse oppgavene til høyre web-API for utførelse en om gangen. JavaScript holder styr på disse oppgavene og håndterer hver i henhold til oppgavens kompleksitetsnivå.

For å forstå behovet for JavaScript-hendelsesløkken og asynkron programmering. Du må forstå hvilket problem det i hovedsak løser.

instagram viewer

Ta denne koden, for eksempel:

functionlongRunningFunction() {
// This function does something that takes a long time to execute.
for (var i = 0; i < 100000; i++) {
console.log("Hello")
}
}

functionshortRunningFunction(a) {
return a * 2 ;
}

functionmain() {
var startTime = Date.now();
longRunningFunction();

var endTime = Date.now();

// Prints the amount of time it took to execute functions
console.log(shortRunningFunction(2));
console.log("Time taken: " + (endTime - startTime) + " milliseconds");
}

main();

Denne koden definerer først en funksjon kalt longRunningFunction(). Denne funksjonen vil gjøre en slags kompleks tidkrevende oppgave. I dette tilfellet utfører den en til loop itererende over 100 000 ganger. Dette betyr at console.log("Hei") går 100 000 ganger over.

Avhengig av datamaskinens hastighet kan dette ta lang tid og blokkere shortRunningFunction() fra umiddelbar utførelse til forrige funksjon fullføres.

For kontekst, her er en sammenligning av tiden det tar å kjøre begge funksjonene:

Og så singelen shortRunningFunction():

Forskjellen mellom en operasjon på 2351 millisekunder og en operasjon på 0 millisekunder er åpenbar når du har som mål å bygge en effektiv app.

Hvordan Event Loop hjelper med appytelse

Eventsløyfen har ulike stadier og deler som bidrar til å få systemet til å fungere.

Anropsstakken

JavaScript-anropsstakken er avgjørende for hvordan JavaScript håndterer funksjons- og hendelseskall fra applikasjonen din. JavaScript-kode kompileres fra topp til bunn. Men, Node.js, ved å lese koden, vil Node.js tildele funksjonskall fra bunnen til toppen. Mens den leser, skyver den de definerte funksjonene som rammer inn i anropsstakken én etter én.

Anropsstakken er ansvarlig for å opprettholde utførelseskonteksten og riktig rekkefølge av funksjoner. Den gjør dette ved å fungere som en Last-In-First-Out (LIFO)-stabel.

Dette betyr at den siste funksjonsrammen programmet ditt skyver inn i anropsstakken vil være den første som hopper av stabelen og kjører. Dette vil sikre at JavaScript opprettholder riktig rekkefølge for funksjonsutførelse.

JavaScript vil sprette hver ramme av stabelen til den er tom, noe som betyr at alle funksjoner er ferdige.

Libuv Web API

Kjernen i JavaScripts asynkrone programmer er libuv. Libuv-biblioteket er skrevet i programmeringsspråket C, som kan samhandle med operativsystemets API-er på lavt nivå. Biblioteket vil gi flere APIer som lar JavaScript-kode kjøre parallelt med andre kode. APIer for å lage tråder, et API for å kommunisere mellom tråder og et API for å administrere trådsynkronisering.

For eksempel når du bruker setTimeout i Node.js for å sette kjøringen på pause. Tidtakeren settes opp gjennom libuv, som administrerer hendelsessløyfen for å utføre tilbakeringingsfunksjonen når den angitte forsinkelsen har passert.

På samme måte, når du utfører nettverksoperasjoner asynkront, håndterer libuv disse operasjonene i en ikke-blokkerende måte, for å sikre at andre oppgaver kan fortsette behandlingen uten å vente på input/output (I/O) operasjonen for å slutt.

Tilbakeringing og hendelseskø

Tilbakeringing og hendelseskøen er der tilbakeringingsfunksjoner venter på utførelse. Når en asynkron operasjon fullføres fra libuv, blir dens tilsvarende tilbakeringingsfunksjon lagt til denne køen.

Slik går sekvensen:

  1. JavaScript flytter asynkrone oppgaver til libuv for å håndtere, og fortsetter å håndtere neste oppgave umiddelbart.
  2. Når den asynkrone oppgaven er ferdig, legger JavaScript til tilbakeringingsfunksjonen til tilbakeringingskøen.
  3. JavaScript fortsetter å utføre andre oppgaver i anropsstakken til det er ferdig med alt i gjeldende rekkefølge.
  4. Når anropsstakken er tom, ser JavaScript på tilbakeringingskøen.
  5. Hvis det er en tilbakeringing i køen, skyver den den første inn i anropsstakken og utfører den.

På denne måten blokkerer ikke asynkrone oppgaver hovedtråden, og tilbakeringingskøen sikrer at deres tilsvarende tilbakeringinger utføres i den rekkefølgen de fullførte.

Event Loop Cycle

Eventløkken har også noe som kalles mikrooppgavekøen. Denne spesielle køen i hendelsesløkken inneholder mikrooppgaver som er planlagt å utføre så snart den gjeldende oppgaven i anropsstakken er fullført. Denne utførelsen skjer før neste gjengivelse eller hendelsesløkkeiterasjon. Mikrooppgaver er høyt prioriterte oppgaver med forrang over vanlige oppgaver i arrangementssløyfen.

En mikrooppgave opprettes ofte når du arbeider med Promises. Når et løfte løses eller avvises, er det tilsvarende .deretter() eller .å fange() tilbakeringinger blir med i mikrooppgavekøen. Du kan bruke den køen til å administrere oppgaver som trenger umiddelbar utførelse etter den gjeldende operasjonen, for eksempel oppdatering av brukergrensesnittet til applikasjonen eller håndtering av tilstandsendringer.

For eksempel en nettapplikasjon som utfører datahenting og oppdaterer brukergrensesnittet basert på de hentede dataene. Brukere kan utløse denne datahentingen ved å klikke på en knapp gjentatte ganger. Hvert knappeklikk starter en asynkron datainnhentingsoperasjon.

Uten mikrooppgaver ville hendelsesløkken for denne oppgaven fungere som følger:

  1. Brukeren klikker på knappen gjentatte ganger.
  2. Hvert knappeklikk utløser en asynkron datahentingsoperasjon.
  3. Når datahentingsoperasjonene er fullført, legger JavaScript til de tilsvarende tilbakeringingene til den vanlige oppgavekøen.
  4. Hendelsesløkken begynner å behandle oppgaver i den vanlige oppgavekøen.
  5. Brukergrensesnittoppdateringen basert på datahentingsresultatene kjøres så snart de vanlige oppgavene tillater det.

Men med mikrooppgaver fungerer hendelsesløkken annerledes:

  1. Brukeren klikker på knappen gjentatte ganger og utløser en asynkron datahentingsoperasjon.
  2. Når datahentingsoperasjonene er fullført, legger hendelsesløkken til sine tilsvarende tilbakeringinger til mikrooppgavekøen.
  3. Hendelsesløkken begynner å behandle oppgaver i mikrooppgavekøen umiddelbart etter å ha fullført gjeldende oppgave (knappeklikk).
  4. Brukergrensesnittoppdateringen basert på datahentingsresultatene kjøres før neste vanlige oppgave, og gir en mer responsiv brukeropplevelse.

Her er et kodeeksempel:

const fetchData = () => {
returnnewPromise(resolve => {
setTimeout(() => resolve('Data from fetch'), 2000);
});
};

document.getElementById('fetch-button').addEventListener('click', () => {
fetchData().then(data => {
// This UI update will run before the next rendering cycle
updateUI(data);
});
});

I dette eksemplet ringer hvert klikk på "Hent"-knappen fetchData(). Hver datahentingsoperasjonsplaner som en mikrooppgave. Basert på de hentede dataene, kjøres UI-oppdateringen umiddelbart etter at hver henteoperasjon er fullført, før andre gjengivelses- eller hendelsesløkkeoppgaver.

Dette sikrer at brukere ser de oppdaterte dataene uten å oppleve forsinkelser på grunn av andre oppgaver i hendelsessløyfen.

Bruk av mikrooppgaver i scenarier som dette kan forhindre UI-jank og gi raskere og jevnere interaksjoner i applikasjonen din.

Implikasjoner av Event Loop for webutvikling

Å forstå hendelsessløyfen og hvordan du bruker funksjonene er avgjørende for å bygge effektive og responsive applikasjoner. Hendelsesløkken gir asynkrone og parallelle muligheter, slik at du effektivt kan håndtere komplekse oppgaver i applikasjonen din uten å gå på bekostning av brukeropplevelsen.

Node.js gir alt du trenger, inkludert nettarbeidere for å oppnå ytterligere parallellitet utenfor JavaScripts hovedtråd.