Alla artiklar

JavaScript event loop – så fungerar asynkron kod

För robotar
4 november 2026
JavaScript event loop – så fungerar asynkron kod

En JavaScript event loop är den grundläggande mekanism som möjliggör asynkron och icke-blockerande programkörning i en entrådad exekveringsmiljö. Genom att samordna minnet, anropsstacken och uppgiftsköerna ser denna loop till att händelser, nätverksförfrågningar och gränssnittsuppdateringar hanteras utan att hela systemet låser sig.

Vad är en JavaScript event loop?

I grunden körs koden i JavaScript i en enda huvudtråd per agent eller process. Det innebär att språket endast kan utföra en instruktion i taget. Om ett program skulle utföra tidskrävande beräkningar eller vänta synkront på svar från en server, skulle hela webbapplikationen eller serverskriptet sluta svara under tiden.

För att lösa detta förlitar sig utvecklingsmiljön på kringliggande värdsystem, såsom webbläsarens API:er eller serverarkitekturen. När en asynkron uppgift startas lämnas arbetet över till bakomliggande processer. Själva loopen har som uppgift att kontinuerligt övervaka om programmet är redo att ta emot nya instruktioner och skicka vidare färdiga återanrop till exekvering.

Anropsstacken och hur exekvering sker

Kärnan i exekveringsmodellen är en anropsstack, vilken fungerar enligt principen sist in, först ut (LIFO). Varje gång en funktion anropas i koden skapas en ny minnesram (stack frame) som läggs överst på stacken. Denna ram innehåller funktionens lokala variabler och information om varifrån anropet kom.

När funktionen har kört klart tas ramen bort från toppen av stacken, och exekveringen återgår till den anropande funktionen. Synkron kod körs alltid färdigt på detta sätt innan något annat kan ske. Om stacken hålls upptagen av en oändlig slinga eller en tung matematisk beräkning blockeras alla andra händelser.

Microtasks och macrotasks i en JavaScript event loop

När asynkrona funktioner blir klara placeras deras svarsfunktioner i särskilda köer. Dessa delas upp i två huvudsakliga kategorier med olika prioritet:

  1. Microtask Queue (mikrouppgifter): Här hamnar återanrop från löften (Promises), satser med async/await samt explicita anrop via queueMicrotask. Mikrouppgifter har alltid företräde. När den aktuella synkrona koden på stacken är tömd kommer körningen att bearbeta alla mikrouppgifter i kön tills den är helt tom, innan något annat tillåts ske.
  2. Macrotask Queue (makrouppgifter): Denna kö kallas ofta för den vanliga händelsekön och hanterar tidsfunktioner som setTimeout och setInterval, användarinteraktioner via händelselyssnare och I/O-händelser. När en makrouppgift har körts kontrolleras mikrokön på nytt innan nästa makrouppgift påbörjas.

Denna strikta prioritering förklarar varför ett uppfyllt Promise alltid körs före en setTimeout med fördröjning satt till noll millisekunder.

Skillnader mellan webbläsare och Node.js

Beroende på var koden körs ser implementationsdetaljerna något annorlunda ut, även om grundprincipen är densamma:

  • I webbläsaren: Här måste hanteringen samspela med renderingen av DOM-element, CSS-beräkningar och skärmens uppdateringsfrekvens. Webbläsaren försöker schemalägga rendering mellan uppgifter för att användarupplevelsen ska förbli mjuk och responsiv.
  • I servermiljön: Plattformen Node.js använder C-biblioteket libuv för att hantera icke-blockerande I/O mot filsystem och nätverk. Här är cykeln indelad i specifika faser, bland annat för timers, väntande systemanrop och stängningshändelser, vilket möjliggör hantering av tusentals samtidiga anslutningar på en enda tråd.

Bästa praxis för att undvika blockering

För att säkerställa hög prestanda i moderna applikationer bör utvecklare följa några etablerade riktlinjer:

  • Undvik att köra tunga och beräkningsintensiva algoritmer direkt på huvudtråden. För CPU-krävande uppgifter bör web workers eller worker threads i bakgrunden användas.
  • Skapa inte oändliga kedjor av mikrouppgifter, eftersom detta svälter ut händelsekön och hindrar webbläsaren från att rita om skärmen eller ta emot klick.
  • Dela upp omfattande databehandling i mindre delar med hjälp av timers eller asynkrona iteratorer så att andra processer hinner exekveras mellan stegen.

Relaterade artiklar