JavaScript on yksisäikeinen kieli, joka on samalla myös lukkiutumaton, asynkroninen ja samanaikainen. Tässä artikkelissa selitetään sinulle, miten se tapahtuu.
Suoritusaika
JavaScript on tulkattu kieli, ei käännetty. Tämä tarkoittaa, että se tarvitsee tulkin, joka muuntaa JS koodi konekoodiksi. Tulkkeja (ns. moottoreita) on useita erilaisia. Suosituimmat selainmoottorit ovat V8 (Chrome), Quantum (Firefox) ja WebKit (Safari). V8:aa käytetään muuten myös suositussa muussa kuin selaimen ajoajassa, Node.js.
Jokainen moottori sisältää muistikasan, kutsupinon, tapahtumasilmukan, takaisinkutsujonon ja WebAPI:n, jossa on HTTP-pyyntöjä, ajastimia, tapahtumia jne., jotka on kaikki toteutettu omalla tavallaan JS-koodin nopeamman ja turvallisemman tulkinnan varmistamiseksi.
JS:n perusarkkitehtuuri. Kirjoittaja: Alex Zlatkov
Yksittäinen lanka
Yksisäikeinen kieli on kieli, jossa on yksi kutsupino ja yksi muistikasa. Se tarkoittaa, että sillä suoritetaan vain yhtä asiaa kerrallaan.
A pino on jatkuva muistialue, joka varaa paikallisen kontekstin jokaiselle suoritettavalle funktiolle.
A kasa on paljon laajempi alue, johon tallennetaan kaikki dynaamisesti varattu.
A kutsupino on tietorakenne, joka periaatteessa tallentaa, missä vaiheessa ohjelmaa olemme.
Kutsupino
Kirjoitetaan yksinkertainen koodi ja seurataan, mitä kutsupinossa tapahtuu.
Kuten näet, funktiot lisätään pinoon, suoritetaan ja myöhemmin poistetaan. Tämä on niin sanottu LIFO-menetelmä - Last In, First Out. Jokaista kutsupinon merkintää kutsutaan pinokehys.
Kutsupinon tunteminen on hyödyllistä virhepinojen jälkien lukemisessa. Yleensä virheen tarkka syy on ensimmäisellä rivillä ylhäällä, vaikka koodin suoritusjärjestys on alhaalta ylöspäin.
Joskus voit käsitellä suosittua virhettä, jonka ilmoittaa Kutsupinon enimmäiskoko ylitetty. Tämä on helppo saada aikaan rekursiolla:
funktio foo() {
foo()
}
foo()
ja selaimemme tai päätelaitteemme jäätyy. Jokaisella selaimella, jopa niiden eri versioilla, on erilainen kutsupinon kokorajoitus. Suurimmassa osassa tapauksia ne ovat riittäviä, ja ongelma on etsittävä muualta.
Estetty kutsupino
Tässä on esimerkki JS-säikeen estämisestä. Yritetään lukea foo tiedosto ja baari käyttämällä Solmu.js synkroninen toiminto readFileSync.
Tämä on silmukoitu GIF. Kuten näet, JS-moottori odottaa, kunnes ensimmäinen kutsu kohdassa readFileSync on valmis. Mutta näin ei tapahdu, koska ei ole olemassa foo tiedostossa, joten toista funktiota ei koskaan kutsuta.
Asynkroninen käyttäytyminen
JS voi kuitenkin olla myös lukkiutumaton ja käyttäytyä kuin se olisi monisäikeinen. Se tarkoittaa, että se ei odota API-kutsun vastausta, I/O-tapahtumia jne. ja voi jatkaa koodin suorittamista. Tämä on mahdollista JS-moottoreiden ansiosta, jotka käyttävät (konepellin alla) todellisia monisäikeisiä kieliä, kuten C++ (Chrome) tai Rust (Firefox). Ne tarjoavat meille Web API:n selaimen hupun alla tai esim. I/O API Node.js:n alla.
Yllä olevasta GIF-kuvasta näemme, että ensimmäinen funktio työnnetään kutsupinoon ja Hei suoritetaan välittömästi konsolissa.
Sitten kutsumme setTimeout selaimen WebAPI:n tarjoama toiminto. Se siirtyy kutsupinoon ja sen asynkroniseen takaisinkutsuun. foo toiminto siirtyy WebApin jonoon, jossa se odottaa kutsua, joka tapahtuu 3 sekunnin kuluttua.
Sillä välin ohjelma jatkaa koodia ja näemme, että Hei. Minua ei ole estetty konsolissa.
Kun sitä kutsutaan, jokainen WebAPI-jonossa oleva toiminto siirtyy komentosarjaan Takaisinkutsujono. Siinä funktiot odottavat, kunnes kutsupino on tyhjä. Kun se tapahtuu, ne siirretään sinne yksi kerrallaan.
Joten, kun meidän setTimeout ajastin lopettaa lähtölaskennan, meidän foo funktio menee takaisinkutsujonoon, odottaa kunnes kutsupino tulee saataville, menee sinne, suoritetaan ja näemme, että Hei asynkronisesta takaisinkutsusta konsolissa.
Tapahtumasilmukka
Kysymys kuuluu, mistä runtime tietää, että kutsupino on tyhjä, ja miten kutsujonossa oleva tapahtuma kutsutaan? Tapahtumasilmukka. Se on osa JS-moottoria. Tämä prosessi tarkistaa jatkuvasti, onko kutsupino tyhjä, ja jos on, se seuraa, onko callback-jonossa tapahtuma, joka odottaa kutsua.
Siinä kaikki taika kulissien takana!
Teorian kiteyttäminen
Rinnakkaisuus ja rinnakkaisuus
Samanaikaisuus tarkoittaa useiden tehtävien suorittamista samanaikaisesti mutta ei samanaikaisesti. Esim. kaksi tehtävää toimii päällekkäisinä ajanjaksoina.
Rinnakkaisuus tarkoittaa kahden tai useamman tehtävän suorittamista samanaikaisesti, esimerkiksi useiden laskutoimitusten suorittamista samanaikaisesti.
Kierteet ja prosessit
Kierteet ovat koodin suoritusjaksoja, jotka voidaan suorittaa toisistaan riippumatta.
Prosessi on käynnissä olevan ohjelman instanssi. Ohjelmalla voi olla useita prosesseja.
Synkroninen ja asynkroninen
Osoitteessa synkroninen ohjelmoinnissa tehtävät suoritetaan peräkkäin. Kukin tehtävä odottaa, että edellinen tehtävä on valmis, ja se suoritetaan vasta sitten.
Osoitteessa asynkroninen ohjelmointi, kun yksi tehtävä on suoritettu, voit siirtyä toiseen tehtävään odottamatta edellisen tehtävän valmistumista.
Synkroninen ja asynkroninen yksisäikeisessä ja monisäikeisessä ympäristössä
Synkroninen yhden säikeen kanssa: Tehtävät suoritetaan yksi toisensa jälkeen. Kukin tehtävä odottaa edellisen tehtävän suorittamista.
Synkroninen useiden säikeiden kanssa: Tehtävät suoritetaan eri säikeissä, mutta ne odottavat muita suoritettavia tehtäviä missä tahansa toisessa säikeessä.
Asynkroninen yhdellä säikeellä: Tehtäviä aletaan suorittaa odottamatta toisen tehtävän valmistumista. Tiettynä aikana voidaan suorittaa vain yksi tehtävä.
Asynkroninen useilla säikeillä: Tehtävät suoritetaan eri säikeissä odottamatta muiden tehtävien valmistumista ja suorittavat suorituksensa itsenäisesti.
JavaScript-luokitus
Jos tarkastelemme, miten JS-moottorit toimivat konepellin alla, voimme luokitella JS:n asynkroniseksi ja yksisäikeiseksi tulkatuksi kieleksi. Sana "tulkittu" on erittäin tärkeä, koska se tarkoittaa, että kieli on aina ajasta riippuvainen eikä koskaan yhtä nopea kuin käännetyt kielet, joissa on sisäänrakennettu monisäikeistäminen.
Huomionarvoista on, että Node.js:llä voidaan toteuttaa todellista monisäikeistystä edellyttäen, että jokainen säie käynnistetään erillisenä prosessina. Tätä varten on olemassa kirjastoja, mutta Node.js:ssä on sisäänrakennettu ominaisuus nimeltä Työntekijän langat.
Kaikki tapahtumasilmukan GIF-kuvat tulevat Luuppi Philip Robertsin luoma sovellus, jossa voit testata asynkronisia skenaarioita.