Kód bázis

Javascript a motorháztető alatt

Javascript fejlesztőként úgy lehet a leghatékonyabban dolgozni, ha az ember tisztában van a forráskódját feldolgozó mechanizmussal.

Ebből a cikkből megtudhatod, hogy hogyan működik a javascript engine és betekintést nyerhetsz a böngésző más fontos alkotóelemeinek tevékenységébe is.


 

Amikor a böngészőbe megérkezik egy javascript forrásfájl, akkor annak végrehajtását a JavaScript Engine intézi.

 

Az engine egyszerre kizárólag csak egy sor kódot értelmez, más szóval szinkron végrehajtódású.

Emellett az a jellemzője, hogy a kódot fentről lefelé sorról sorra hajtja végre.

Az, hogy éppen hol tart éppen a végrehajtódás, más szóval melyik függvénynél van a kontroll, a call stack nevű mechanizmusban van nyilvántartva.

A call stack úgy működik, hogy amikor felszólítunk egy function-t a végrehajtódásra, akkor az a függvény rátolódik a call stack-re és amikor végetért a végrehajtódása, akkor lekerül a stack-ről.


 

Az engine, a program végrehajtását a globális kód futtatásával kezdi, ezt konvenció szerint main-nek, vagy global-nak nevezzük.

Amikor a globális kontextusban ráfutunk a console.log function hívásra, akkor végrehajtódik a log function, tehát rákerül a stack-re, majd amikor lefutott lekerül onnan.

Ezután meghívódik az első függvény így az kerül rá a call stack-re és megkapja a kontrollt.

A végrehajtódás ilyen módon folytatódik tovább, mindig amelyik függvényt meghívódik ahhoz kerül a kontroll és ha lefutott, akkor visszakerül a kontroll abba a függvény kontextusba, ahonnan meg lett hívva.

Végsősoron, amikor minden sor kód végrehajtódott a globális kontextusban is, akkor a script futása végetér.

 

 

 

Az addEventListener function

Felmerülhet azonban a kérdés, hogy hogyan lehet hogy a javascriptes programokban a működés sokszor párhuzamosnak tűnik?

Például mi történik az engine-ben akkor, amikor gombnyomás eseményre hajtódik végre valami funkcionalitás?

 

A válasz erre a következő:

A javascript engine képes feladatokat rábízni az őt körülölelő böngésző programra.

Mindig amikor valami eseményre kell reagálni, vagy egy hosszú ideig tartó műveletet kell végrehajtani, az engine beregisztrálja az adott feladatot a böngészőnek

A böngésző az engine futásával párhuzamosan el tudja látni a rábízott feladatokat, ilyen módon az enginnek nem kell leállnia foglalkozni ezekkel a műveletekkel.

Ha közvetlenül ő fogalalkozna velük, akkor azzal hosszú ideig blokkva lenne a stack és egy időre megszűnne az oldalunkon minden interaktív működés.

A web api-k összefoglaló néven a böngésző által nyújtott azon eszköztár, aminek segítségével az engine feladatokat tud beregisztrálni.

 

Mindig amikor valamely beregisztált feladat kapcsán valami fejlemény történik, a böngésző a callback queue-ba helyez el üzeneteket.
Amíg az engine-ben tart a szinkron futás, addig a queue-ban lévő üzenetek a bekerülés sorrendjében várakoznak.

A felgyűlő üzenetek kapcsán elvégzendő feladatok végrehajtását az event loop nevű eszköz indítványozza.

Az event loop úgy működik, hogy folyamatosan nézi egyszerre a stack-et és a callback queue-t  és amint azt látja, hogy a stack kiürült és a callback queue-ban éppen van elem, akkor kiveszi a soron következő elemet a queue-ból és rátolja a stackre.

 

 

Itt a szokásos szinkron működést láthatjuk, viszont ez esetben az addEventlistener függvény futása során, az engine beregisztrál egy feladatot a böngészőnek, majd folytatja a többi kód végrehajtását.

Végsősoron a call stack kiürül.

 

Innentől kezdve akármikor klikk esemény történik a megadott elementre, a böngésző berak egy értesítést a callback queue-ba.

Az event loop ha éppen nem lát a stack-en semmit, akkor kiszedi a soron következő elemet a queue-ból és rátolja a stack-re.

 

Ezután ismét szinkron végrehajtódás veszi kezdetét, egészen addig amíg le nem ürül a stack.

Ha leürült, akkor a loop további elemeket tesz rá és ez így folytatódik, egészen addig amíg a queue ki nem ürült.

 

 

A web apik révén megannyi más hasonló jellegű feladat beregisztrálható, ezekkel munkád során sokszor találkozhatsz.

Ide tartozik a setTimeout, a setInterval és az AJAX kérések is.

 

 

A setTimeout function

Egy utolsó példa; mi történik ha meghívjuk a setTimeout function-t, bedobjuk a késleltetni kívánt függvényt és 0ms-ot adunk meg második paraméterként.

Elsőre azt gondolhatnánk, hogy a bedobott függvény azonnal futni fog, mert a 0ms azonnal letelik, de valójában az előbb felvázolt mechanizmus szerint fog alalkulni a kód futása.

 

Tehát a setTimeout function hívásával regisztrálunk egy feladatot a böngészőnek és a 0ms megadásával csak annyi történik, hogy a callback queue-ba szinte azon nyomban bekerül az üzenet.

 

Viszont miután az event loop csak a stack leürülése után tesz rá elemet a stack-re, a setTimeoutba dobott függvény végrehajtódása később fog történni.

Ezt a logban is láthatjuk, a második function-ben kilogolt üzenetek íródnak ki utoljára.

 

 

Ha ezt a felvázolt rendszert átlátod, akkor elkerülheted azt a tipikus hibát, hogy a kód szinkron módon lefutó részéből próbálsz hivatkozni egy olyan értékre, ami csak a jövőben lesz majd elérhető.

 

Tekintsd meg a teljes videót: