JavaScript on ühetäheline keel ja samal ajal ka mitteblokeeriv, asünkroonne ja samaaegne. Selles artiklis selgitatakse teile, kuidas see toimub.
Käitusaeg
JavaScript on tõlgitud keel, mitte kompileeritud keel. See tähendab, et see vajab interpretaatorit, mis teisendab JS kood masinkoodiks. On olemas mitut tüüpi interpreteerijaid (tuntud kui mootorid). Kõige populaarsemad brauserite mootorid on V8 (Chrome), Quantum (Firefox) ja WebKit (Safari). Muide, V8 on kasutusel ka populaarses mitte-brauseri jooksutusprogrammis, Node.js.
Iga mootor sisaldab mäluhunnikut, kutsete virna, sündmuste ahelat, tagasikutsumise järjekorda ja WebAPI-d koos HTTP päringute, taimerite, sündmuste jne, mis on kõik omal viisil rakendatud JS-koodi kiiremaks ja turvalisemaks tõlgendamiseks.
Põhiline JS-ajastusarhitektuur. Autor: Alex Zlatkov
Üksik niit
Ühe niidi keel on keel, millel on ainult üks kutsete virn ja ainult üks mäluhunnik. See tähendab, et korraga töötab ainult üks asi.
A virna on pidev mälupiirkond, mis eraldab lokaalse konteksti igale täidetavale funktsioonile.
A hunnik on palju suurem piirkond, kus hoitakse kõike dünaamiliselt eraldatud teavet.
A kõnede virna on andmestruktuur, mis põhimõtteliselt salvestab, kus me programmis oleme.
Kõne Stack
Kirjutame lihtsa koodi ja jälgime, mis toimub kutsete virnas.
Nagu näete, lisatakse funktsioonid virna, täidetakse ja hiljem kustutatakse. See on nn LIFO-viis - Last In, First Out. Iga kirjet call stackis nimetatakse virnaraam.
Teadmised kõnede virna kohta on kasulikud vigade virna jälgede lugemiseks. Üldiselt on täpne vea põhjus esimesel real üleval, kuigi koodi täitmise järjekord on alt ülespoole.
Mõnikord saab tegeleda populaarse veaga, millest teavitab Maksimaalne kutsete virna suurus on ületatud. Seda on lihtne saavutada rekursiooni abil:
funktsioon foo() {
foo()
}
foo()
ja meie brauser või terminal külmutab. Igal brauseril, isegi nende erinevatel versioonidel, on erinev call stacki suuruse piirang. Enamikul juhtudel on need piisavad ja probleemi tuleb otsida mujalt.
Blokeeritud kõnede virna
Siin on näide JS-niidi blokeerimisest. Proovime lugeda foo faili ja baar kasutades Sõlme.js sünkroonne funktsioon readFileSync.
See on loopitud GIF. Nagu näete, ootab JS-mootor kuni esimese kutsumiseni aadressil readFileSync on lõpetatud. Kuid seda ei juhtu, sest ei ole olemas foo faili, nii et teist funktsiooni ei kutsuta kunagi.
Asünkroonne käitumine
Kuid JS võib olla ka mitteblokeeriv ja käituda nii, nagu oleks see mitmikeeruline. See tähendab, et ta ei oota API-kõne vastust, I/O-sündmusi jne ja võib jätkata koodi täitmist. See on võimalik tänu JS-mootoritele, mis kasutavad (kapoti all) tõelisi mitmikeelseid keeli, nagu C++ (Chrome) või Rust (Firefox). Nad pakuvad meile veebi API-d brauseri kapuutsi all või nt. I/O API-d Node.js all.
Ülaltoodud GIF-is näeme, et esimene funktsioon lükatakse call stacki ja Tere täidetakse kohe konsoolis.
Seejärel kutsume me setTimeout funktsioon, mida pakub brauseri WebAPI. See läheb kutsete virna ja selle asünkroonne tagasikutsumine foo funktsioon läheb WebApi järjekorda, kus see ootab kõne, mis on määratud toimuma 3 sekundi pärast.
Vahepeal jätkab programm koodi ja me näeme, et Tere. Ma ei ole blokeeritud konsoolis.
Pärast selle käivitamist läheb iga funktsioon WebAPI järjekorras olevasse funktsiooni Tagasikutsumise järjekord. See on koht, kus funktsioonid ootavad, kuni kutsete virn on tühi. Kui see juhtub, viiakse nad sinna ükshaaval.
Nii et kui meie setTimeout taimer lõpetab tagasiarvestuse, meie foo funktsioon läheb tagasilöögijärjekorda, ootab, kuni kutsete virna vabaneb, läheb sinna, täidetakse ja me näeme, et Tere asünkroonsest tagasikutsumisest konsoolis.
Sündmuse tsükkel
Küsimus on selles, kuidas saab runtime teada, et kutsete virn on tühi ja kuidas kutsutakse tagasi kutsete järjekorras olev sündmus esile? Tutvuge sündmuse ahelaga. See on osa JS-mootorist. See protsess kontrollib pidevalt, kas call stack on tühi ja kui on, siis jälgib, kas callbacki järjekorras on sündmus, mis ootab kutsumist.
See on kogu võlu kulisside taga!
Teooria kokkuvõtteks
Samaaegsus ja paralleelsus
Samaaegsus tähendab mitme ülesande samaaegset, kuid mitte samaaegset täitmist. Näiteks kaks ülesannet töötab kattuvas ajaperioodis.
Paralleelsus tähendab kahe või enama ülesande samaaegset täitmist, nt mitme arvutuse samaaegset sooritamist.
Niidid ja protsessid
Niidid on koodide täitmise jada, mida saab täita üksteisest sõltumatult.
Protsess on käimasoleva programmi instants. Programmil võib olla mitu protsessi.
Sünkroonne ja asünkroonne
Veebilehel sünkroonne programmeerimisel täidetakse ülesandeid üksteise järel. Iga ülesanne ootab, kuni eelmine ülesanne on lõpetatud, ja seda täidetakse alles siis.
Veebilehel asünkroonne programmeerimine, kui üks ülesanne on täidetud, saate vahetada teise ülesande vastu, ootamata eelmise ülesande täitmist.
Sünkroonne ja asünkroonne ühe- ja mitmeahelaline keskkond
Sünkroonne ühe niidiga: Ülesanded täidetakse üksteise järel. Iga ülesanne ootab, kuni eelmine ülesanne on täidetud.
Sünkroonne mitme niidiga: Ülesandeid täidetakse erinevates niitides, kuid oodatakse mis tahes muid ülesandeid, mis on täitmisel mõnes teises niidis.
Asünkroonne ühe niidiga: Ülesandeid hakatakse täitma ilma teise ülesande lõpetamist ootamata. Ühel ajahetkel saab täita ainult ühte ülesannet.
Asünkroonne mitme niidiga: Ülesandeid täidetakse erinevates niitides, ootamata teiste ülesannete lõpetamist ja lõpetades nende täitmise iseseisvalt.
JavaScript klassifikatsioon
Kui me vaatame, kuidas JS-mootorid kapoti all töötavad, siis võime JS-i liigitada asünkroonseks ja ühe niidiga interpreteeritavaks keeleks. Sõna "interpreteeritud" on väga oluline, sest see tähendab, et keel on alati tööajast sõltuv ja mitte kunagi nii kiire kui kompileeritud keeled, millel on sisseehitatud mitmehäälsus.
On märkimisväärne, et Node.js suudab saavutada tõelise mitmikeeramise, tingimusel, et iga niit käivitatakse eraldi protsessina. Selleks on olemas raamatukogud, kuid Node.js-l on sisseehitatud funktsioon nimega Töötajate niidid.
Kõik sündmuse loop GIFid pärinevad Luup Philip Robertsi loodud rakendus, kus saate testida oma asünkroonseid stsenaariume.