PadziļinÄta JavaScript notikumu cilpas, uzdevumu rindu un mikrouzdevumu rindu izpÄte, izskaidrojot, kÄ JavaScript nodroÅ”ina vienlaicÄ«gumu un reakciju vienas pavediena vidÄs.
JavaScript notikumu cilpas demistifikÄcija: Uzdevumu rindas un mikrouzdevumu pÄrvaldÄ«bas izpratne
JavaScript, neskatoties uz to, ka ir viena pavediena valoda, spÄj efektÄ«vi apstrÄdÄt vienlaicÄ«gumu un asinhronÄs operÄcijas. To nodroÅ”ina Ä£eniÄlÄ Notikumu cilpa. Izpratne par to, kÄ tas darbojas, ir bÅ«tiska jebkuram JavaScript izstrÄdÄtÄjam, kura mÄrÄ·is ir rakstÄ«t veiktspÄjÄ«gas un reaÄ£ÄjoÅ”as lietojumprogrammas. Å is visaptveroÅ”ais ceļvedis izpÄtÄ«s notikumu cilpas sarežģītÄ«bas, koncentrÄjoties uz Uzdevumu rindu (pazÄ«stama arÄ« kÄ AtzvanīŔanas rinda) un Mikrouzdevumu rindu.
Kas ir JavaScript notikumu cilpa?
Notikumu cilpa ir nepÄrtraukti darbojoÅ”s process, kas uzrauga izsaukuma kaudzi un uzdevumu rindu. TÄs galvenÄ funkcija ir pÄrbaudÄ«t, vai izsaukuma kaudze ir tukÅ”a. Ja tÄ ir, notikumu cilpa paÅem pirmo uzdevumu no uzdevumu rindas un ievieto to izsaukuma kaudzÄ izpildei. Å is process atkÄrtojas bezgalÄ«gi, ļaujot JavaScript Ŕķietami vienlaikus apstrÄdÄt vairÄkas operÄcijas.
IedomÄjieties to kÄ rÅ«pÄ«gu darbinieku, kas pastÄvÄ«gi pÄrbauda divas lietas: "Vai es paÅ”laik kaut ko daru (izsaukuma kaudze)?" un "Vai man ir jÄpaveic kaut kas jÄgaida (uzdevumu rinda)?" Ja darbinieks ir neaktÄ«vs (izsaukuma kaudze ir tukÅ”a) un ir uzdevumi, kas jÄgaida (uzdevumu rinda nav tukÅ”a), darbinieks paÅem nÄkamo uzdevumu un sÄk to risinÄt.
BÅ«tÄ«bÄ notikumu cilpa ir dzinÄjs, kas ļauj JavaScript veikt nebloÄ·ÄjoÅ”as operÄcijas. Bez tÄs JavaScript varÄtu ierobežot koda secÄ«gu izpildi, kas var novest pie sliktas lietotÄja pieredzes, Ä«paÅ”i tÄ«mekļa pÄrlÅ«kprogrammÄs un Node.js vidÄs, kas nodarbojas ar I/O operÄcijÄm, lietotÄju mijiedarbÄ«bu un citiem asinhroniem notikumiem.
Izsaukuma kaudze: kur kods tiek izpildīts
Izsaukuma kaudze ir datu struktÅ«ra, kas ievÄro Last-In, First-Out (LIFO) principu. Å Ä« ir vieta, kur faktiski tiek izpildÄ«ts JavaScript kods. Kad funkcija tiek izsaukta, tÄ tiek ievietota izsaukuma kaudzÄ. Kad funkcija pabeidz savu izpildi, tÄ tiek izÅemta no kaudzes.
Apsveriet Å”o vienkÄrÅ”o piemÄru:
function firstFunction() {
console.log('PirmÄ funkcija');
secondFunction();
}
function secondFunction() {
console.log('OtrÄ funkcija');
}
firstFunction();
LÅ«k, kÄ izsaukuma kaudze izskatÄ«tos izpildes laikÄ:
- SÄkotnÄji izsaukuma kaudze ir tukÅ”a.
firstFunction()tiek izsaukta un ievietota kaudzÄ.- IekÅ”Ä
firstFunction(),console.log('PirmÄ funkcija')tiek izpildÄ«ts. secondFunction()tiek izsaukta un ievietota kaudzÄ (virsfirstFunction()).- IekÅ”Ä
secondFunction(),console.log('OtrÄ funkcija')tiek izpildÄ«ts. secondFunction()pabeidz un tiek izÅemta no kaudzes.firstFunction()pabeidz un tiek izÅemta no kaudzes.- Izsaukuma kaudze tagad atkal ir tukÅ”a.
Ja funkcija atkÄrtoti izsauc sevi bez pareiza izieÅ”anas nosacÄ«juma, tas var novest pie Kaudzes pÄrpildes kļūdas, kad izsaukuma kaudze pÄrsniedz savu maksimÄlo lielumu, izraisot programmas avÄriju.
Uzdevumu rinda (AtzvanīŔanas rinda): Asinhrono operÄciju apstrÄde
Uzdevumu rinda (pazÄ«stama arÄ« kÄ AtzvanīŔanas rinda vai Makrouzdevumu rinda) ir uzdevumu rinda, kas gaida apstrÄdi ar notikumu cilpu. To izmanto, lai apstrÄdÄtu asinhronÄs operÄcijas, piemÄram:
setTimeoutunsetIntervalatzvanīŔanas funkcijas- Notikumu klausÄ«tÄji (piemÄram, klikŔķu notikumi, taustiÅu nospieÅ”anas notikumi)
XMLHttpRequest(XHR) unfetchatzvanīŔanas funkcijas (tÄ«kla pieprasÄ«jumiem)- LietotÄja mijiedarbÄ«bas notikumi
Kad asinhronÄ operÄcija ir pabeigta, tÄs atzvanīŔanas funkcija tiek ievietota Uzdevumu rindÄ. PÄc tam notikumu cilpa paÅem Ŕīs atzvanīŔanas funkcijas pa vienai un izpilda tÄs izsaukuma kaudzÄ, kad tÄ ir tukÅ”a.
ParÄdÄ«sim to ar setTimeout piemÄru:
console.log('SÄkums');
setTimeout(() => {
console.log('Timeout atzvanīŔana');
}, 0);
console.log('Beigas');
JÅ«s varÄtu sagaidÄ«t, ka izvade bÅ«s:
SÄkums
Timeout atzvanīŔana
Beigas
TomÄr faktiskÄ izvade ir:
SÄkums
Beigas
Timeout atzvanīŔana
Å eit ir iemesls:
console.log('SÄkums')tiek izpildÄ«ts un reÄ£istrÄ "SÄkums".setTimeout(() => { ... }, 0)tiek izsaukts. Kaut arÄ« aizkave ir 0 milisekundes, atzvanīŔanas funkcija netiek izpildÄ«ta nekavÄjoties. TÄ vietÄ tÄ tiek ievietota Uzdevumu rindÄ.console.log('Beigas')tiek izpildÄ«ts un reÄ£istrÄ "Beigas".- Izsaukuma kaudze tagad ir tukÅ”a. Notikumu cilpa pÄrbauda Uzdevumu rindu.
- AtzvanīŔanas funkcija no
setTimeouttiek pÄrvietota no Uzdevumu rindas uz Izsaukuma kaudzi un izpildÄ«ta, reÄ£istrÄjot "Timeout atzvanīŔana".
Tas parÄda, ka pat ar 0 ms aizkavi, setTimeout atzvanīŔanas funkcijas vienmÄr tiek izpildÄ«tas asinhroni, pÄc tam, kad ir pabeigts paÅ”reizÄjais sinhronais kods.
Mikrouzdevumu rinda: augstÄka prioritÄte nekÄ Uzdevumu rindai
Mikrouzdevumu rinda ir vÄl viena rinda, ko pÄrvalda notikumu cilpa. TÄ ir paredzÄta uzdevumiem, kas jÄizpilda pÄc iespÄjas ÄtrÄk pÄc paÅ”reizÄjÄ uzdevuma pabeigÅ”anas, bet pirms notikumu cilpa atkal atveido vai apstrÄdÄ citus notikumus. Uzskatiet to par augstÄkas prioritÄtes rindu salÄ«dzinÄjumÄ ar Uzdevumu rindu.
Bieži mikrouzdevumu avoti ietver:
- Promises:
.then(),.catch()un.finally()Promises atzvanīŔanas funkcijas tiek pievienotas Mikrouzdevumu rindai. - MutationObserver: Izmanto, lai novÄrotu izmaiÅas DOM (Document Object Model). IzmaiÅu novÄrotÄja atzvanīŔanas funkcijas tiek pievienotas arÄ« Mikrouzdevumu rindai.
process.nextTick()(Node.js): PlÄno atzvanīŔanas funkciju, kas jÄizpilda pÄc paÅ”reizÄjÄs operÄcijas pabeigÅ”anas, bet pirms notikumu cilpa turpinÄs. Lai gan spÄcÄ«ga, tÄs pÄrmÄrÄ«ga izmantoÅ”ana var novest pie I/O izsalkuma.queueMicrotask()(RelatÄ«vi jauns pÄrlÅ«ka API): StandartizÄts veids, kÄ ievietot mikrouzdevumu rindÄ.
GalvenÄ atŔķirÄ«ba starp Uzdevumu rindu un Mikrouzdevumu rindu ir tÄda, ka notikumu cilpa apstrÄdÄ visus pieejamos mikrouzdevumus Mikrouzdevumu rindÄ, pirms paÅem nÄkamo uzdevumu no Uzdevumu rindas. Tas nodroÅ”ina, ka mikrouzdevumi tiek izpildÄ«ti nekavÄjoties pÄc katra uzdevuma pabeigÅ”anas, samazinot iespÄjamÄs aizkaves un uzlabojot reakciju.
Apsveriet Å”o piemÄru, kas ietver Promises un setTimeout:
console.log('SÄkums');
Promise.resolve().then(() => {
console.log('Promise atzvanīŔana');
});
setTimeout(() => {
console.log('Timeout atzvanīŔana');
}, 0);
console.log('Beigas');
Izvade būs:
SÄkums
Beigas
Promise atzvanīŔana
Timeout atzvanīŔana
Šeit ir sadalījums:
console.log('SÄkums')tiek izpildÄ«ts.Promise.resolve().then(() => { ... })izveido atrisinÄtu Promise..then()atzvanīŔanas funkcija tiek pievienota Mikrouzdevumu rindai.setTimeout(() => { ... }, 0)pievieno savu atzvanīŔanas funkciju Uzdevumu rindai.console.log('Beigas')tiek izpildÄ«ts.- Izsaukuma kaudze ir tukÅ”a. Notikumu cilpa vispirms pÄrbauda Mikrouzdevumu rindu.
- Promise atzvanīŔanas funkcija tiek pÄrvietota no Mikrouzdevumu rindas uz Izsaukuma kaudzi un izpildÄ«ta, reÄ£istrÄjot "Promise atzvanīŔana".
- Mikrouzdevumu rinda tagad ir tukÅ”a. PÄc tam notikumu cilpa pÄrbauda Uzdevumu rindu.
setTimeoutatzvanīŔanas funkcija tiek pÄrvietota no Uzdevumu rindas uz Izsaukuma kaudzi un izpildÄ«ta, reÄ£istrÄjot "Timeout atzvanīŔana".
Å is piemÄrs skaidri parÄda, ka mikrouzdevumi (Promise atzvanīŔanas funkcijas) tiek izpildÄ«ti pirms uzdevumiem (setTimeout atzvanīŔanas funkcijas), pat ja setTimeout aizkave ir 0.
PrioritÄÅ”u noteikÅ”anas nozÄ«me: Mikrouzdevumi vs. Uzdevumi
Mikrouzdevumu prioritÄtes noteikÅ”ana attiecÄ«bÄ pret uzdevumiem ir bÅ«tiska, lai uzturÄtu reaÄ£ÄjoÅ”u lietotÄja interfeisu. Mikrouzdevumi bieži ietver operÄcijas, kas jÄizpilda pÄc iespÄjas ÄtrÄk, lai atjauninÄtu DOM vai apstrÄdÄtu kritiskas datu izmaiÅas. ApstrÄdÄjot mikrouzdevumus pirms uzdevumiem, pÄrlÅ«kprogramma var nodroÅ”inÄt, ka Å”ie atjauninÄjumi tiek atspoguļoti Ätri, uzlabojot lietojumprogrammas uztverto veiktspÄju.
PiemÄram, iedomÄjieties situÄciju, kad atjauninÄt lietotÄja interfeisu, pamatojoties uz datiem, kas saÅemti no servera. Izmantojot Promises (kas izmanto Mikrouzdevumu rindu), lai apstrÄdÄtu datu apstrÄdi un lietotÄja interfeisa atjauninÄjumus, tiek nodroÅ”inÄts, ka izmaiÅas tiek piemÄrotas Ätri, nodroÅ”inot vienmÄrÄ«gÄku lietotÄja pieredzi. Ja Å”iem atjauninÄjumiem izmantotu setTimeout (kas izmanto Uzdevumu rindu), varÄtu bÅ«t pamanÄma aizkave, kas varÄtu novest pie mazÄk reaÄ£ÄjoÅ”as lietojumprogrammas.
Izsalkums: Kad mikrouzdevumi bloÄ·Ä notikumu cilpu
Lai gan Mikrouzdevumu rinda ir paredzÄta reakcijas uzlaboÅ”anai, ir svarÄ«gi to izmantot apdomÄ«gi. Ja jÅ«s nepÄrtraukti pievienojat mikrouzdevumus rindai, neļaujot notikumu cilpai pÄriet uz Uzdevumu rindu vai atveidot atjauninÄjumus, jÅ«s varat izraisÄ«t izsalkumu. Tas notiek, ja Mikrouzdevumu rinda nekad nekļūst tukÅ”a, efektÄ«vi bloÄ·Äjot notikumu cilpu un novÄrÅ”ot citu uzdevumu izpildi.
Apsveriet Å”o piemÄru (galvenokÄrt attiecas uz tÄdÄm vidÄm kÄ Node.js, kur process.nextTick ir pieejams, bet konceptuÄli piemÄrojams citur):
function starve() {
Promise.resolve().then(() => {
console.log('Mikrouzdevums izpildīts');
starve(); // AtkÄrtoti pievieno vÄl vienu mikrouzdevumu
});
}
starve();
Å ajÄ piemÄrÄ funkcija starve() nepÄrtraukti pievieno jaunas Promise atzvanīŔanas funkcijas Mikrouzdevumu rindai. Notikumu cilpa iestrÄgs, bezgalÄ«gi apstrÄdÄjot Å”os mikrouzdevumus, novÄrÅ”ot citu uzdevumu izpildi un potenciÄli novedot pie iesaldÄtas lietojumprogrammas.
LabÄkÄ prakse, lai izvairÄ«tos no izsalkuma:
- Ierobežojiet mikrouzdevumu skaitu, kas izveidoti viena uzdevuma ietvaros. Izvairieties no mikrouzdevumu rekursÄ«viem cikliem, kas var bloÄ·Ät notikumu cilpu.
- Apsveriet iespÄju izmantot
setTimeoutmazÄk kritisku operÄciju veikÅ”anai. Ja operÄcijai nav nepiecieÅ”ama tÅ«lÄ«tÄja izpilde, atliekot to Uzdevumu rindai, varat novÄrst Mikrouzdevumu rindas pÄrslogoÅ”anu. - Å emiet vÄrÄ mikrouzdevumu ietekmi uz veiktspÄju. Lai gan mikrouzdevumi parasti ir ÄtrÄki par uzdevumiem, pÄrmÄrÄ«ga lietoÅ”ana joprojÄm var ietekmÄt lietojumprogrammas veiktspÄju.
ReÄlÄs pasaules piemÄri un lietoÅ”anas gadÄ«jumi
PiemÄrs 1: Asinhrona attÄla ielÄde ar Promises
function loadImage(url) {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => resolve(img);
img.onerror = () => reject(new Error(`NeizdevÄs ielÄdÄt attÄlu vietnÄ ${url}`));
img.src = url;
});
}
// PiemÄrs lietoÅ”anai:
loadImage('https://example.com/image.jpg')
.then(img => {
// AttÄls veiksmÄ«gi ielÄdÄts. Atjauniniet DOM.
document.body.appendChild(img);
})
.catch(error => {
// ApstrÄdÄjiet attÄla ielÄdes kļūdu.
console.error(error);
});
Å ajÄ piemÄrÄ funkcija loadImage atgriež Promise, kas atrisina, kad attÄls ir veiksmÄ«gi ielÄdÄts, vai noraida, ja rodas kļūda. .then() un .catch() atzvanīŔanas funkcijas tiek pievienotas Mikrouzdevumu rindai, nodroÅ”inot, ka DOM atjauninÄÅ”ana un kļūdu apstrÄde tiek izpildÄ«ta nekavÄjoties pÄc attÄla ielÄdes operÄcijas pabeigÅ”anas.
PiemÄrs 2: MutationObserver izmantoÅ”ana dinamiskajiem lietotÄja interfeisa atjauninÄjumiem
const observer = new MutationObserver(mutations => {
mutations.forEach(mutation => {
console.log('IzmaiÅas novÄrotas:', mutation);
// Atjauniniet lietotÄja interfeisu, pamatojoties uz izmaiÅÄm.
});
});
const elementToObserve = document.getElementById('myElement');
observer.observe(elementToObserve, {
attributes: true,
childList: true,
subtree: true
});
// VÄlÄk modificÄjiet elementu:
elementToObserve.textContent = 'Jauns saturs!';
MutationObserver ļauj jums uzraudzÄ«t izmaiÅas DOM. Kad notiek izmaiÅas (piemÄram, tiek mainÄ«ts atribÅ«ts, tiek pievienots bÄrnu mezgls), MutationObserver atzvanīŔanas funkcija tiek pievienota Mikrouzdevumu rindai. Tas nodroÅ”ina, ka lietotÄja interfeiss tiek Ätri atjauninÄts, reaÄ£Äjot uz DOM izmaiÅÄm.
PiemÄrs 3: TÄ«kla pieprasÄ«jumu apstrÄde ar Fetch API
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => {
console.log('Dati saÅemti:', data);
// ApstrÄdÄjiet datus un atjauniniet lietotÄja interfeisu.
})
.catch(error => {
console.error('Kļūda datu iegūŔanÄ:', error);
// ApstrÄdÄjiet kļūdu.
});
Fetch API ir moderns veids, kÄ veikt tÄ«kla pieprasÄ«jumus JavaScript. .then() atzvanīŔanas funkcijas tiek pievienotas Mikrouzdevumu rindai, nodroÅ”inot, ka datu apstrÄde un lietotÄja interfeisa atjauninÄjumi tiek izpildÄ«ti, tiklÄ«dz atbilde ir saÅemta.
Node.js notikumu cilpas apsvÄrumi
Notikumu cilpa Node.js darbojas lÄ«dzÄ«gi pÄrlÅ«kprogrammas videi, bet tai ir dažas specifiskas funkcijas. Node.js izmanto bibliotÄku libuv, kas nodroÅ”ina notikumu cilpas ievieÅ”anu kopÄ ar asinhronÄm I/O iespÄjÄm.
process.nextTick(): KÄ minÄts iepriekÅ”, process.nextTick() ir Node.js specifiska funkcija, kas ļauj ieplÄnot atzvanīŔanas funkciju, kas jÄizpilda pÄc paÅ”reizÄjÄs operÄcijas pabeigÅ”anas, bet pirms notikumu cilpa turpinÄs. AtzvanīŔanas funkcijas, kas pievienotas ar process.nextTick(), tiek izpildÄ«tas pirms Promise atzvanīŔanas funkcijÄm Mikrouzdevumu rindÄ. TomÄr izsalkuma iespÄjamÄ«bas dÄļ process.nextTick() jÄizmanto taupÄ«gi. queueMicrotask() parasti ir vÄlams, ja tas ir pieejams.
setImmediate(): Funkcija setImmediate() ieplÄno atzvanīŔanas funkciju, kas jÄizpilda nÄkamajÄ notikumu cilpas iterÄcijÄ. Tas ir lÄ«dzÄ«gi setTimeout(() => { ... }, 0), bet setImmediate() ir paredzÄts ar I/O saistÄ«tiem uzdevumiem. Izpildes secÄ«ba starp setImmediate() un setTimeout(() => { ... }, 0) var bÅ«t neprognozÄjama un ir atkarÄ«ga no sistÄmas I/O veiktspÄjas.
LabÄkÄ prakse efektÄ«vai notikumu cilpas pÄrvaldÄ«bai
- Izvairieties no galvenÄ pavediena bloÄ·ÄÅ”anas. IlgstoÅ”as sinhronÄs operÄcijas var bloÄ·Ät notikumu cilpu, padarot lietojumprogrammu nereaÄ£ÄjoÅ”u. Izmantojiet asinhronÄs operÄcijas, kad vien iespÄjams.
- OptimizÄjiet savu kodu. EfektÄ«vs kods tiek izpildÄ«ts ÄtrÄk, samazinot laiku, kas pavadÄ«ts izsaukuma kaudzÄ, un ļaujot notikumu cilpai apstrÄdÄt vairÄk uzdevumu.
- Izmantojiet Promises asinhronÄm operÄcijÄm. Promises nodroÅ”ina tÄ«rÄku un pÄrvaldÄmÄku veidu, kÄ apstrÄdÄt asinhrono kodu, salÄ«dzinot ar tradicionÄlajÄm atzvanīŔanas funkcijÄm.
- Apzinaties Mikrouzdevumu rindu. Izvairieties no pÄrmÄrÄ«gu mikrouzdevumu izveides, kas var novest pie izsalkuma.
- Izmantojiet Web Workers aprÄÄ·inu ietilpÄ«giem uzdevumiem. Web Workers ļauj palaist JavaScript kodu atseviŔķos pavedienos, novÄrÅ”ot galvenÄ pavediena bloÄ·ÄÅ”anu. (PÄrlÅ«kprogrammas vides specifika)
- ProfilÄjiet savu kodu. Izmantojiet pÄrlÅ«kprogrammas izstrÄdÄtÄja rÄ«kus vai Node.js profilÄÅ”anas rÄ«kus, lai identificÄtu veiktspÄjas Å”aurÄs vietas un optimizÄtu savu kodu.
- Debounce un throttle notikumus. Notikumiem, kas tiek palaisti bieži (piemÄram, ritinÄÅ”anas notikumi, izmÄru maiÅas notikumi), izmantojiet debouncingu vai throttling, lai ierobežotu to reižu skaitu, cik reizes notikuma apstrÄdÄtÄjs tiek izpildÄ«ts. Tas var uzlabot veiktspÄju, samazinot slodzi uz notikumu cilpu.
SecinÄjums
JavaScript notikumu cilpas, Uzdevumu rindas un Mikrouzdevumu rindas izpratne ir bÅ«tiska, lai rakstÄ«tu veiktspÄjÄ«gas un reaÄ£ÄjoÅ”as JavaScript lietojumprogrammas. Izprotot, kÄ darbojas notikumu cilpa, jÅ«s varat pieÅemt apzinÄtus lÄmumus par to, kÄ apstrÄdÄt asinhronÄs operÄcijas un optimizÄt savu kodu labÄkai veiktspÄjai. Atcerieties pareizi noteikt mikrouzdevumu prioritÄti, izvairÄ«ties no izsalkuma un vienmÄr censties atbrÄ«vot galveno pavedienu no bloÄ·ÄÅ”anas operÄcijÄm.
Å is ceļvedis ir sniedzis visaptveroÅ”u pÄrskatu par JavaScript notikumu cilpu. Izmantojot Å”eit aprakstÄ«tÄs zinÄÅ”anas un labÄko praksi, jÅ«s varat izveidot stabilas un efektÄ«vas JavaScript lietojumprogrammas, kas nodroÅ”ina lielisku lietotÄja pieredzi.