JavaScripts exekveringsmodell är nyanserad och lätt att missförstå. Att lära sig mer om händelseslingan i dess kärna kan hjälpa.

JavaScript är ett entrådigt språk, byggt för att hantera uppgifter en i taget. Händelseloopen låter dock JavaScript hantera händelser och återuppringningar asynkront genom att emulera samtidiga programmeringssystem. Detta säkerställer prestandan för dina JavaScript-applikationer.

Vad är JavaScript Event Loop?

JavaScripts händelseloop är en mekanism som körs i bakgrunden av varje JavaScript-applikation. Det tillåter JavaScript att hantera uppgifter i sekvens utan att blockera dess huvudexekveringstråd. Detta kallas asynkron programmering.

Händelseloopen håller en kö av uppgifter att köra och matar dessa uppgifter till höger webb-API för utförande en i taget. JavaScript håller reda på dessa uppgifter och hanterar var och en enligt uppgiftens komplexitetsnivå.

För att förstå behovet av JavaScript-händelsslingan och asynkron programmering. Du måste förstå vilket problem det i huvudsak löser.

instagram viewer

Ta den här koden, till exempel:

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();

Denna kod definierar först en funktion som kallas longRunningFunction(). Denna funktion kommer att göra någon form av komplex tidskrävande uppgift. I det här fallet utför den en för loop itererande över 100 000 gånger. Detta innebär att console.log("Hej") går 100 000 gånger om.

Beroende på datorns hastighet kan detta ta lång tid och blockera shortRunningFunction() från omedelbar körning tills föregående funktion är klar.

För sammanhang, här är en jämförelse av tiden det tar att köra båda funktionerna:

Och så singeln shortRunningFunction():

Skillnaden mellan en operation på 2 351 millisekund och en operation på 0 millisekund är uppenbar när du siktar på att bygga en presterande app.

Hur Event Loop hjälper till med appprestanda

Eventslingan har olika stadier och delar som bidrar till att få systemet att fungera.

Call Stack

JavaScript-anropsstacken är väsentlig för hur JavaScript hanterar funktions- och händelseanrop från din applikation. JavaScript-kod kompileras från topp till botten. Men, Node.js, vid läsning av koden, kommer Node.js att tilldela funktionsanrop från botten till toppen. När den läser trycker den in de definierade funktionerna som ramar i anropsstacken en efter en.

Anropsstacken ansvarar för att upprätthålla exekveringskontexten och korrekt ordning på funktioner. Den gör detta genom att fungera som en Last-In-First-Out (LIFO) stack.

Detta betyder att den sista funktionsramen som ditt program skjuter in i anropsstacken kommer att vara den första som hoppar av stacken och körs. Detta kommer att säkerställa att JavaScript upprätthåller rätt ordning för funktionsexekvering.

JavaScript kommer att ta bort varje bildruta från stacken tills den är tom, vilket betyder att alla funktioner har körts färdigt.

Libuv Web API

Kärnan i JavaScripts asynkrona program är libuv. Libuv-biblioteket är skrivet i programmeringsspråket C, som kan interagera med operativsystemets API: er på låg nivå. Biblioteket kommer att tillhandahålla flera API: er som gör att JavaScript-kod kan köras parallellt med andra koda. API: er för att skapa trådar, ett API för att kommunicera mellan trådar och ett API för att hantera trådsynkronisering.

Till exempel när du använder setTimeout i Node.js för att pausa körningen. Timern ställs in via libuv, som hanterar händelseslingan för att utföra återuppringningsfunktionen när den angivna fördröjningen har passerat.

På liknande sätt, när du utför nätverksoperationer asynkront, hanterar libuv dessa operationer i en icke-blockerande sätt, vilket säkerställer att andra uppgifter kan fortsätta bearbetningen utan att vänta på in-/utgångsoperationen (I/O) slutet.

Callback & Event Queue

Återuppringnings- och händelsekön är där återuppringningsfunktioner väntar på exekvering. När en asynkron operation slutförs från libuv, läggs dess motsvarande återuppringningsfunktion till i denna kö.

Så här går sekvensen:

  1. JavaScript flyttar asynkrona uppgifter till libuv för att den ska hantera, och fortsätter att hantera nästa uppgift omedelbart.
  2. När den asynkrona uppgiften är klar lägger JavaScript till sin återuppringningsfunktion till återuppringningskön.
  3. JavaScript fortsätter att utföra andra uppgifter i anropsstacken tills det är klart med allt i den aktuella ordningen.
  4. När samtalsstacken är tom tittar JavaScript på återuppringningskön.
  5. Om det finns en återuppringning i kön, skjuter den den första till samtalsstacken och kör den.

På så sätt blockerar inte asynkrona uppgifter huvudtråden, och återuppringningskön säkerställer att deras motsvarande återuppringningar körs i den ordning de slutförde.

Händelseloopcykeln

Händelseloopen har också något som kallas microtask-kön. Denna speciella kö i händelseslingan innehåller mikrouppgifter som är schemalagda att utföras så snart den aktuella uppgiften i anropsstacken är klar. Denna körning sker före nästa rendering eller händelseloopiteration. Mikrouppgifter är högprioriterade uppgifter med företräde framför vanliga uppgifter i evenemangsslingan.

En mikrouppgift skapas vanligtvis när man arbetar med Promises. Närhelst ett löfte löser sig eller förkastas, motsvarar det det .sedan() eller .fånga() callbacks ansluter sig till microtask-kön. Du kan använda den kön för att hantera uppgifter som behöver köras omedelbart efter den aktuella åtgärden, som att uppdatera gränssnittet för din applikation eller hantera tillståndsändringar.

Till exempel en webbapplikation som utför datahämtning och uppdaterar användargränssnittet baserat på hämtad data. Användare kan utlösa denna datahämtning genom att klicka på en knapp upprepade gånger. Varje knappklick initierar en asynkron datahämtning.

Utan mikrouppgifter skulle händelseloopen för denna uppgift fungera enligt följande:

  1. Användaren klickar på knappen upprepade gånger.
  2. Varje knappklick utlöser en asynkron datahämtningsoperation.
  3. När datahämtningen är klar lägger JavaScript till motsvarande återuppringningar till den vanliga uppgiftskön.
  4. Händelseloopen börjar bearbeta uppgifter i den vanliga uppgiftskön.
  5. Användargränssnittsuppdateringen baserad på datahämtningsresultaten körs så snart de vanliga uppgifterna tillåter det.

Men med mikrouppgifter fungerar händelseslingan annorlunda:

  1. Användaren klickar på knappen upprepade gånger och utlöser en asynkron datahämtningsoperation.
  2. När datahämtningsoperationerna är klara lägger händelseslingan till sina motsvarande återuppringningar till mikrouppgiftskön.
  3. Händelseloopen börjar bearbeta uppgifter i mikrouppgiftskön omedelbart efter att ha slutfört den aktuella uppgiften (knappklick).
  4. Användargränssnittsuppdateringen baserad på datahämtningsresultaten körs före nästa vanliga uppgift, vilket ger en mer responsiv användarupplevelse.

Här är ett kodexempel:

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 det här exemplet ringer varje klick på "Hämta"-knappen fetchData(). Varje datahämtningsoperationsschema som en mikrouppgift. Baserat på hämtad data körs UI-uppdateringen omedelbart efter att varje hämtningsoperation har slutförts, före alla andra renderings- eller händelseloopuppgifter.

Detta säkerställer att användare ser den uppdaterade datan utan att uppleva några förseningar på grund av andra uppgifter i händelseslingan.

Att använda mikrouppgifter i scenarier som detta kan förhindra UI-skräck och ge snabbare och smidigare interaktioner i din applikation.

Implikationer av Event Loop för webbutveckling

Att förstå händelseslingan och hur man använder dess funktioner är avgörande för att bygga prestanda och responsiva applikationer. Händelseloopen ger asynkrona och parallella möjligheter, så att du effektivt kan hantera komplexa uppgifter i din applikation utan att kompromissa med användarupplevelsen.

Node.js tillhandahåller allt du behöver, inklusive webbarbetare för att uppnå ytterligare parallellism utanför JavaScripts huvudtråd.