JavaScript ir vienpavedienu valoda, kas vienlaikus ir arī nebloķējoša, asinhrona un vienlaicīga. Šajā rakstā tiks izskaidrots, kā tas notiek.
Runtime
JavaScript ir interpretēta, nevis kompilēta valoda. Tas nozīmē, ka tai ir nepieciešams tulks, kas konvertē JSkods mašīnkodā. Ir vairāku veidu tulki (t. s. dzinēji). Populārākie pārlūkprogrammu dzinēji ir V8 (Chrome), Quantum (Firefox) un WebKit (Safari). Starp citu, V8 tiek izmantots arī populārajā palīgprogrammā, kas nav pārlūkprogramma, Node.js.
Katrā dzinējā ir atmiņas kaudze, izsaukumu kaudze, notikumu cilpa, atsaukumu rinda un WebAPI ar HTTP pieprasījumiem, taimeriem, notikumiem u. c., kas visi ir ieviesti savā veidā, lai ātrāk un drošāk interpretētu JS kodu.
JS izpildes laika arhitektūras pamati. Autors: Autors: Alex Zlatkov
Viena vītne
Viena pavediena valoda ir valoda ar vienu izsaukumu kaudzi un vienu atmiņas kaudzi. Tas nozīmē, ka tajā vienlaikus tiek izpildīta tikai viena darbība.
A kaudze ir nepārtraukts atmiņas apgabals, kurā katrai izpildītajai funkcijai tiek piešķirts vietējais konteksts.
A kaudze ir daudz lielāks reģions, kurā tiek glabāts viss dinamiski piešķirtais.
A izsaukumu kaudze ir dati struktūra, kurā būtībā ir ierakstīts, kur mēs atrodamies programmā.
Zvanu kaudze
Uzrakstīsim vienkāršu kodu un novērosim, kas notiek izsaukumu kaudzē.
Kā redzat, funkcijas tiek pievienotas kaudzē, izpildītas un vēlāk dzēstas. Tas ir tā sauktais LIFO veids - Pēdējais iekšā, pirmais ārā. Katru izsaukumu kaudzes ierakstu sauc par kaudzes rāmis.
Zināšanas par izsaukumu kaudzīti ir noderīgas, lai lasītu kļūdu kaudzītes izsekojumus. Parasti precīzs kļūdas iemesls ir pirmās rindas augšpusē, lai gan koda izpildes secība ir no apakšas uz augšu.
Dažreiz var tikt galā ar populāru kļūdu, par kuru paziņo Pārsniegts maksimālais izsaukumu kaudzes lielums. To var viegli iegūt, izmantojot rekursiju:
funkcija foo() {
foo()
}
foo()
un mūsu pārlūkprogramma vai termināls sastingst. Katrai pārlūkprogrammai, pat to dažādajām versijām, ir atšķirīgs izsaukumu kaudzes lieluma ierobežojums. Lielākajā daļā gadījumu tie ir pietiekami, un problēma jāmeklē citur.
Bloķēts izsaukumu kaudze
Šeit ir JS pavediena bloķēšanas piemērs. Mēģināsim nolasīt foo failu un bārs izmantojot Mezgls.js sinhronā funkcija readFileSync.
Šis ir cilpveida GIF. Kā redzat, JS dzinējs gaida līdz pirmajam izsaukumam no readFileSync ir pabeigta. Bet tas nenotiks, jo nav foo failu, tāpēc otrā funkcija nekad netiks izsaukta.
Asinhronā uzvedība
Tomēr JS var arī nebloķēt un darboties tā, it kā tas būtu daudzpavedienu lietojums. Tas nozīmē, ka tas negaida atbildi no API izsaukumiem, I/O notikumiem u.c. un var turpināt koda izpildi. Tas ir iespējams, pateicoties JS dzinējiem, kas izmanto (zem pārsega) reālas daudzkārtu valodas, piemēram, C++ (Chrome) vai Rust (Firefox). Tās nodrošina mums ar Tīmekļa vietne API zem pārlūkprogrammas pārsegiem vai ex. I/O API zem Node.js.
Iepriekš redzamajā GIF attēlā redzams, ka pirmā funkcija tiek ievietota izsaukumu kaudzē un Sveiki nekavējoties tiek izpildīts konsoles logā.
Pēc tam mēs izsaucam setTimeout funkcija, ko nodrošina pārlūkprogrammas WebAPI. Tā nonāk izsaukumu kaudzē un tās asinhronajā atpakaļsaukumā foo funkcija nonāk WebApi rindā, kur tā gaida izsaukumu, kas tiek iestatīts pēc 3 sekundēm.
Tikmēr programma turpina kodu, un mēs redzam. Sveiki. Es neesmu bloķēts konsolē.
Pēc izsaukšanas katra WebAPI rindā esošā funkcija tiek nosūtīta uz Atgriezenisko zvanu rinda. Tā ir vieta, kur funkcijas gaida, līdz izsaukumu kaudze ir tukša. Kad tas notiek, tās tur tiek pārvietotas viena pēc otras.
Tātad, kad mūsu setTimeout taimeris pabeidz atpakaļskaitīšanu, mūsu foo funkcija dodas uz atpakaļsaukumu rindu, gaida, kamēr izsaukumu kaudze kļūst pieejama, dodas uz to, tiek izpildīta un mēs redzam. Sveiki no asinhronā atgriezeniskā izsaukuma konsolē.
Notikumu cilpa
Jautājums ir par to, kā izpildmehānisms uzzina, ka izsaukumu kaudze ir tukša, un kā tiek izsaukts notikums izsaukumu atgriezeniskās saites rindā? Iepazīstieties ar notikumu cilpu. Tā ir JS dzinēja daļa. Šis process nepārtraukti pārbauda, vai izsaukumu kaudze ir tukša, un, ja tā ir, uzrauga, vai izsaukumu atgriezeniskās saites rindā ir notikums, kas gaida, lai to izsauktu.
Tā ir visa burvība aizkulisēs!
Teorijas kopsavilkums
Vienlaicīgums un paralēlisms
Vienlaicīgums tas nozīmē, ka vienlaicīgi tiek veikti vairāki uzdevumi, bet ne vienlaicīgi. Piemēram, divi uzdevumi tiek izpildīti laika periodos, kas pārklājas.
Paralēlisms ir divu vai vairāku uzdevumu veikšana vienlaicīgi, piemēram, vairāku aprēķinu veikšana vienlaicīgi.
Diegi un procesi
Diegi ir koda izpildes secība, ko var izpildīt neatkarīgi vienu no otra.
Process ir darbojošās programmas gadījums. Programmai var būt vairāki procesi.
Sinhronais un asinhronais
In sinhronais programmēšana, uzdevumi tiek izpildīti viens pēc otra. Katrs uzdevums gaida, kamēr tiek pabeigts iepriekšējais uzdevums, un tikai tad tiek izpildīts.
In asinhronais programmēšana, kad ir izpildīts viens uzdevums, varat pārslēgties uz citu uzdevumu, negaidot, kamēr tiks pabeigts iepriekšējais.
Sinhronā un asinhronā darbība vienpavediena un daudzpavedienu vidē
Sinhronais ar vienu pavedienu: Uzdevumi tiek izpildīti viens pēc otra. Katrs uzdevums gaida, kamēr tiks izpildīts iepriekšējais uzdevums.
Sinhronais ar vairākiem pavedieniem: Uzdevumi tiek izpildīti dažādos pavedienos, bet gaida jebkuru citu uzdevumu izpildi jebkurā citā pavedienā.
Asinhronā režīmā ar vienu pavedienu: Uzdevumi tiek sākti izpildīt, negaidot, kamēr tiks pabeigts cits uzdevums. Vienā brīdī var izpildīt tikai vienu uzdevumu.
Asinhronais ar vairākiem pavedieniem: Uzdevumi tiek izpildīti dažādos pavedienos, negaidot citu uzdevumu pabeigšanu un pabeidzot to izpildi neatkarīgi.
JavaScript klasifikācija
Ja aplūkojam, kā JS dzinēji darbojas zem pārsega, JS varam klasificēt kā asinhronu un viena pavediena interpretētu valodu. Vārds “interpretēta” ir ļoti svarīgs, jo tas nozīmē, ka valoda vienmēr būs atkarīga no izpildes laika un nekad nebūs tik ātra kā kompilētās valodas ar iebūvētu daudzkārtu lietojumu.
Jāatzīmē, ka Node.js var panākt reālu daudzpavedienu darbību, ja katrs pavediens tiek palaists kā atsevišķs process. Šim nolūkam ir bibliotēkas, bet Node.js ir iebūvēta funkcija, ko sauc par Strādnieku pavedieni.
Visi notikumu cilpas GIF faili nāk no Lupa Filipa Robertsa izveidotā lietojumprogramma, kurā varat pārbaudīt savus asinhronos scenārijus.