Mi fán terem a JavaScript Event Loop?

Események Olvasási idő:2 perc

A JavaScript az egyik legelterjedtebb nyelv a világon. A StackOverflow 2019-es felmérése szerint pedig a legnépszerűbb nyelv, és ez már sorban a hetedik éve van így, ami nem véletlen, hiszen ez a nyelv a dinamikus webfejlesztéshez elengedhetetlen.

A nyelv

Nagyon sokan használják, de kevesen értik igazán a működését. Nem típusos szkript nyelv, ami azt jelenti, hogy egy szöveges értékkel definiált változó értékül kaphat egy számot vagy épp egy objektumot is. Emiatt nagyon kell figyelni arra, mit is csinálunk. A nyelv tele van ellentmondásokkal és olykor nagyon furcsa működést tud produkálni. Pár példa erre az érdekes működésre:

  • 1 + 0.2 == 0.3 // false
  • true + true + true === 3 // true
  • [] == 0 // true

Nézzünk a felszín alá, hogy lássuk hogyan is működik pontosan ez a különleges nyelv!

Javascript runtime

Az alábbi ábrán láthatjuk a Javascript runtime-ot:

Dorsum-Javasript

Mit is láthatunk itt?

Memory Heap —itt történik a memóriaallokáció

Call Stack — itt vannak a verem keretek a kód végrehajtása közben

Web APIs — ezek a kiegészítő webes funkciók, melyek nem tartoznak bele a JavaScript engine-be, a böngésző saját engine-je szolgáltatja számunkra (NodeJs esetében a Node környezet)

Callback Queue — speciális függvények, amelyek egy úgynevezett Event Table-ben tárolódnak. Innen csak meghatározott esemény bekövetkezésekor kerülnek ki.

Event Loop — egy folyamat, amely folyamatosan ellenőrzi, hogy a call stack üres-e, és amikor üres, ellenőrzi, hogy az event queue-ban vannak-e hívásra várakozó funkciók

Event Loop

A JavaScript runtime kompenensek közül az Event Loop a legérdekesebb, ugyanis ez fogja meghatározni, hogy hogyan működik a parancsok végrehajtása JavaScript-ben.

A JavaScript alapvetően egyszálú környezetben fut, ezért nem tud egyidejűleg több eseményt végrehajtani, a hívások sorrendjében hajtja végre az utasításokat. Azonban az EventLoop és a Web Api-k segítségével mégis tud aszinkron működni. Na de mégis hogyan?

Magát az Event Loop-ot legegyszerűbben úgy tudjuk elképzelni, mint egy végtelen ciklust, ami folyamatosan ellenőrzi, hogy üres-e a call stack, és ha igen, akkor van-e végrehajtandó utasítás az úgynevezett Event Queue-ban. Ahogy a neve is elárulja, ez egy egyszerű FIFO sor. Hogy ne legyen ennyire egyszerű a helyzet, ez valójában kettő darab sor, egy task (Időzítések, DOM események) és micro-task (pl.: Promise, böngésző megfigyelők) sor. A micro-task sor prioritása nagyobb, tehát, ha itt van végrehajtandó parancs, az Event Loop mindig innen veszi ki, ha ez a sor üres, akkor fordul a task sorhoz.

Nézzünk egy egyszerű példát:

Dorsum-event loop példa

A kód futtatása után, amit a konzolon látunk: „elso, masodik, harmadik” – ilyen sorrendben kiírva. Ami itt történik: bekerül a call stack-re az első sor, ami végre is hajtódik és kiírja a szöveget, utána a setTimeout függvény kerül a stack-re, ugyanúgy végrehajtódik, de ez nem ír ki semmit egyelőre, hanem mivel ez egy web api, a böngésző kezeli és elindít egy időzítőt. Végül pedig az 5. sorban található utasítás kerül a stack-re, majd hajtódik végre egyből. Ha lejár a setTimeout időzítője, akkor a myCallback függvény bekerül az Event Queue-ba, azon belül is a task queue-ba. Mivel a stack-en nincs végrehajtandó utasítás, az Event Loop kiveszi ezt a függvényt a sorból és végrehajtja. Érdekes megfigyelni, hogy nem számít, hogy várakozási időnek 0 miliszekundumot definiáltunk, a stack-re kerülő utasítások mindig előbb hajtódnak végre, így ami egyszer bekerült az Event Queue-ba, annak meg kell várnia minden utasítást, ami a stack-re kerül.

Ebből látszik, hogy a setTimeout függvény nem garantálja a végrehajtás idejét, csak a minimális várakozási időt definiálja.

Egy jó példa ennek a használatára, amikor valami hosszan tartó, lassú folyamatot szeretnénk végrehajtani. Ha csak simán kipakoljuk a stack-re az utasításokat, akkor a böngészőnek nem lesz esélye frissíteni a UI-t. Azonban, ha setTimeout segítségével a lassú utasítások bekerülnek az Event Queue-ba, azon belül a task queue-ba, akkor a micro-task queue-ban szereplő DOM frissítési utasítások végrehajtásra kerülnek egy-egy Event Queue-n található utasítás között. Pontosan ezért van az is, hogy egy http hívás automatikusan az Event Queue-ra kerül.

Konklúzió

Ahhoz, hogy igazán jól működő kódot írhassunk, mindig érdemes megvizsgálni, hogyan is működik alapjaiban az adott technológia. Látható, hogy az Event Loop sem fekete mágia, példákon keresztül könnyen megérthető, és ennek tudatában sokkal effektívebb kódot tudunk írni.

A Dorsumnál, a befektetési banki IT világban, a WealthTech szakmában sokat foglalkozunk azzal, hogy naprakészek legyünk a legmodernebb technológiákban és ezáltal ügyfeleinknek is naprakész UX-szel ellátott szoftvereket készítsünk, mint például amihez a cikkben említett JavaScriptet is tudjuk használni. Tudj meg többet arról, hogy mivel foglalkozunk!