JavaScript event loopi, vazifalar navbati va mikro-vazifalar navbatini chuqur oʻrganish, JavaScript bir oqimli muhitda qanday qilib konkurensiya va javob berishga erishishini tushuntirish.
Demystifying the JavaScript Event Loop: Understanding Task Queues and Microtask Management
JavaScript, bir oqimli til bo'lishiga qaramay, konkurensiya va asinxron operatsiyalarni samarali boshqaradi. Bunga ajoyib Event Loop orqali erishiladi. Uning qanday ishlashini tushunish, unumdor va sezgir dasturlarni yozishni maqsad qilgan har qanday JavaScript dasturchisi uchun juda muhimdir. Ushbu keng qamrovli qo'llanma Event Loopning murakkabliklarini o'rganadi va Task Queue (Callback Queue sifatida ham tanilgan) va Microtask Queuega e'tibor qaratadi.
JavaScript Event Loop nima?
Event Loop - bu qo'ng'iroqlar stekini va vazifalar navbatini kuzatib boradigan uzluksiz ishlaydigan jarayon. Uning asosiy vazifasi qo'ng'iroqlar stekining bo'shligini tekshirishdir. Agar shunday bo'lsa, Event Loop vazifalar navbatidan birinchi vazifani oladi va uni bajarish uchun qo'ng'iroqlar stekiga yuklaydi. Bu jarayon cheksiz takrorlanadi va JavaScriptga bir nechta operatsiyalarni bir vaqtning o'zida bajarishga imkon beradi.
Buni doimiy ravishda ikki narsani tekshiradigan tirishqoq ishchi deb o'ylang: "Hozirda biror narsa ustida ishlayapmanmi (qo'ng'iroqlar steki)?" va "Meni bajarishimni kutayotgan narsa bormi (vazifalar navbati)?" Agar ishchi bo'sh bo'lsa (qo'ng'iroqlar steki bo'sh) va vazifalar kutayotgan bo'lsa (vazifalar navbati bo'sh emas), ishchi keyingi vazifani oladi va uning ustida ishlay boshlaydi.
Aslida, Event Loop - bu JavaScriptga blokirovka qilmaydigan operatsiyalarni bajarishga imkon beradigan dvigateldir. Usiz JavaScript kodni ketma-ket bajarish bilan cheklanib qoladi, bu esa, ayniqsa, I/O operatsiyalari, foydalanuvchi o'zaro ta'siri va boshqa asinxron voqealar bilan shug'ullanadigan veb-brauzerlar va Node.js muhitlarida yomon foydalanuvchi tajribasiga olib keladi.
Qo'ng'iroqlar steki: Kod qayerda ishlaydi
Qo'ng'iroqlar steki - bu Last-In, First-Out (LIFO) prinsipiga amal qiladigan ma'lumotlar tuzilmasi. Bu JavaScript kodi haqiqatan ham bajariladigan joy. Funksiya chaqirilganda, u Qo'ng'iroqlar stekiga yuklanadi. Funksiya bajarilishini tugatganda, u stekdan chiqariladi.
Ushbu oddiy misolni ko'rib chiqing:
function firstFunction() {
console.log('Birinchi funksiya');
secondFunction();
}
function secondFunction() {
console.log('Ikkinchi funksiya');
}
firstFunction();
Bu yerda Qo'ng'iroqlar steki bajarilish vaqtida qanday ko'rinishi:
- Dastlab, Qo'ng'iroqlar steki bo'sh.
firstFunction()chaqiriladi va stekga yuklanadi.firstFunction()ichidaconsole.log('Birinchi funksiya')bajariladi.secondFunction()chaqiriladi va stekga yuklanadi (firstFunction()tepasida).secondFunction()ichidaconsole.log('Ikkinchi funksiya')bajariladi.secondFunction()tugaydi va stekdan chiqariladi.firstFunction()tugaydi va stekdan chiqariladi.- Qo'ng'iroqlar steki endi yana bo'sh.
Agar funksiya o'zini to'g'ri chiqish shartisiz rekursiv ravishda chaqirsa, u Stack Overflow xatosiga olib kelishi mumkin, bunda Qo'ng'iroqlar steki maksimal hajmidan oshib ketadi va dastur ishdan chiqadi.
Vazifalar navbati (Callback Queue): Asinxron operatsiyalarni boshqarish
Vazifalar navbati (Callback Queue yoki Macrotask Queue sifatida ham tanilgan) - bu Event Loop tomonidan qayta ishlanishini kutayotgan vazifalar navbati. U asinxron operatsiyalarni boshqarish uchun ishlatiladi:
setTimeoutvasetIntervalcallbacklari- Voqea tinglovchilari (masalan, click voqealari, klaviatura voqealari)
XMLHttpRequest(XHR) vafetchcallbacklari (tarmoq so'rovlari uchun)- Foydalanuvchi o'zaro ta'sir voqealari
Asinxron operatsiya tugallanganda, uning callback funksiyasi Vazifalar navbatiga joylashtiriladi. Keyin Event Loop ushbu callbacklarni birma-bir oladi va Qo'ng'iroqlar stekida u bo'sh bo'lganda bajaradi.
Keling, buni setTimeout misoli bilan ko'rsatamiz:
console.log('Boshlash');
setTimeout(() => {
console.log('Timeout callback');
}, 0);
console.log('Tugatish');
Chiqish quyidagicha bo'lishini kutishingiz mumkin:
Boshlash
Timeout callback
Tugatish
Biroq, haqiqiy chiqish quyidagicha:
Boshlash
Tugatish
Timeout callback
Nima uchun:
console.log('Boshlash')bajariladi va "Boshlash" jurnaliga yoziladi.setTimeout(() => { ... }, 0)chaqiriladi. Kechikish 0 millisekund bo'lsa ham, callback funksiyasi darhol bajarilmaydi. Buning o'rniga, u Vazifalar navbatiga joylashtiriladi.console.log('Tugatish')bajariladi va "Tugatish" jurnaliga yoziladi.- Qo'ng'iroqlar steki endi bo'sh. Event Loop Vazifalar navbatini tekshiradi.
setTimeoutdan callback funksiyasi Vazifalar navbatidan Qo'ng'iroqlar stekiga ko'chiriladi va bajariladi va "Timeout callback" jurnaliga yoziladi.
Bu hatto 0 ms kechikish bilan ham, setTimeout callbacklari har doim asinxron ravishda, joriy sinxron kod bajarilgandan so'ng bajarilishini ko'rsatadi.
Mikro-vazifalar navbati: Vazifalar navbatidan yuqori ustuvorlik
Mikro-vazifalar navbati - bu Event Loop tomonidan boshqariladigan yana bir navbat. U joriy vazifa tugallangandan so'ng, lekin Event Loop qayta render qilmasdan yoki boshqa voqealarni boshqarmasdan imkon qadar tezroq bajarilishi kerak bo'lgan vazifalar uchun mo'ljallangan. Buni Vazifalar navbati bilan solishtirganda yuqori ustuvorlikka ega navbat deb o'ylang.
Mikro-vazifalarning umumiy manbalari quyidagilarni o'z ichiga oladi:
- Promises: Promisesning
.then(),.catch()va.finally()callbacklari Mikro-vazifalar navbatiga qo'shiladi. - MutationObserver: DOMda (Document Object Model) o'zgarishlarni kuzatish uchun ishlatiladi. Mutation observer callbacklari ham Mikro-vazifalar navbatiga qo'shiladi.
process.nextTick()(Node.js): Joriy operatsiya tugallangandan so'ng, lekin Event Loop davom etmasdan oldin callbackni bajarishni rejalashtiradi. Kuchli bo'lsa-da, uni haddan tashqari ishlatish I/O ochligiga olib kelishi mumkin.queueMicrotask()(Nisbatan yangi brauzer API): Mikro-vazifani navbatga qo'yishning standartlashtirilgan usuli.
Vazifalar navbati va Mikro-vazifalar navbati o'rtasidagi asosiy farq shundaki, Event Loop Vazifalar navbatidan keyingi vazifani olishdan oldin Mikro-vazifalar navbatidagi barcha mavjud mikro-vazifalarni qayta ishlaydi. Bu mikro-vazifalarning har bir vazifa tugallangandan so'ng darhol bajarilishini ta'minlaydi, potentsial kechikishlarni minimallashtiradi va javob berishni yaxshilaydi.
Promises va setTimeout ni o'z ichiga olgan ushbu misolni ko'rib chiqing:
console.log('Boshlash');
Promise.resolve().then(() => {
console.log('Promise callback');
});
setTimeout(() => {
console.log('Timeout callback');
}, 0);
console.log('Tugatish');
Chiqish quyidagicha bo'ladi:
Boshlash
Tugatish
Promise callback
Timeout callback
Bu yerda tahlil:
console.log('Boshlash')bajariladi.Promise.resolve().then(() => { ... })hal qilingan Promiseni yaratadi..then()callbacki Mikro-vazifalar navbatiga qo'shiladi.setTimeout(() => { ... }, 0)o'z callbackini Vazifalar navbatiga qo'shadi.console.log('Tugatish')bajariladi.- Qo'ng'iroqlar steki bo'sh. Event Loop birinchi navbatda Mikro-vazifalar navbatini tekshiradi.
- Promise callbacki Mikro-vazifalar navbatidan Qo'ng'iroqlar stekiga ko'chiriladi va bajariladi va "Promise callback" jurnaliga yoziladi.
- Mikro-vazifalar navbati endi bo'sh. Keyin Event Loop Vazifalar navbatini tekshiradi.
setTimeoutcallbacki Vazifalar navbatidan Qo'ng'iroqlar stekiga ko'chiriladi va bajariladi va "Timeout callback" jurnaliga yoziladi.
Ushbu misol aniq ko'rsatadiki, mikro-vazifalar (Promise callbacklari) vazifalardan (setTimeout callbacklari) oldin bajariladi, hatto setTimeout kechikishi 0 bo'lganda ham.
Ustuvorlikning muhimligi: Mikro-vazifalar vs. Vazifalar
Mikro-vazifalarning vazifalardan ustunligi sezgir foydalanuvchi interfeysini saqlash uchun juda muhimdir. Mikro-vazifalar ko'pincha DOMni yangilash yoki muhim ma'lumotlar o'zgarishlarini boshqarish uchun imkon qadar tezroq bajarilishi kerak bo'lgan operatsiyalarni o'z ichiga oladi. Mikro-vazifalarni vazifalardan oldin qayta ishlash orqali brauzer ushbu yangilanishlarning tezda aks etishini ta'minlaydi, bu esa dasturning sezilgan unumdorligini oshiradi.
Misol uchun, siz serverdan olingan ma'lumotlar asosida UI yangilayotgan vaziyatni tasavvur qiling. Ma'lumotlarni qayta ishlash va UI yangilanishlarini boshqarish uchun Promisesdan (Mikro-vazifalar navbatidan foydalanadigan) foydalanish o'zgarishlarning tezda qo'llanilishini ta'minlaydi va foydalanuvchi uchun yanada silliq tajriba taqdim etadi. Agar siz ushbu yangilanishlar uchun setTimeout dan (Vazifalar navbatidan foydalanadigan) foydalansangiz, sezilarli kechikish bo'lishi mumkin, bu esa kamroq sezgir dasturga olib keladi.
Ochlik: Mikro-vazifalar Event Loopni bloklaganda
Mikro-vazifalar navbati javob berishni yaxshilash uchun mo'ljallangan bo'lsa-da, uni oqilona ishlatish juda muhimdir. Agar siz Event Loopga Vazifalar navbatiga o'tishiga yoki yangilanishlarni render qilishiga ruxsat bermasdan, navbatga doimiy ravishda mikro-vazifalar qo'shsangiz, siz ochlikka sabab bo'lishingiz mumkin. Bu Mikro-vazifalar navbati hech qachon bo'sh bo'lmaganda, aslida Event Loopni bloklab, boshqa vazifalarning bajarilishiga to'sqinlik qilganda sodir bo'ladi.
Ushbu misolni ko'rib chiqing (birinchi navbatda, Node.js kabi muhitlarda process.nextTick mavjud bo'lgan joyda tegishli, ammo boshqa joylarda ham qo'llaniladi):
function starve() {
Promise.resolve().then(() => {
console.log('Mikro-vazifa bajarildi');
starve(); // Rekursiv ravishda yana bir mikro-vazifa qo'shing
});
}
starve();
Ushbu misolda starve() funksiyasi doimiy ravishda Mikro-vazifalar navbatiga yangi Promise callbacklarini qo'shadi. Event Loop ushbu mikro-vazifalarni cheksiz qayta ishlashda qolib ketadi, boshqa vazifalarning bajarilishiga to'sqinlik qiladi va potentsial ravishda muzlagan dasturga olib keladi.
Ochlikdan qochish uchun eng yaxshi amaliyotlar:
- Bitta vazifa ichida yaratilgan mikro-vazifalar sonini cheklang. Event Loopni bloklashi mumkin bo'lgan mikro-vazifalarning rekursiv halqalarini yaratishdan saqlaning.
- Kamroq muhim operatsiyalar uchun
setTimeoutdan foydalanishni o'ylab ko'ring. Agar operatsiya darhol bajarilishni talab qilmasa, uni Vazifalar navbatiga o'tkazish Mikro-vazifalar navbatining ortiqcha yuklanishiga yo'l qo'ymaydi. - Mikro-vazifalarning unumdorlikka ta'sirini hisobga oling. Mikro-vazifalar odatda vazifalarga qaraganda tezroq bo'lsa-da, haddan tashqari foydalanish dastur unumdorligiga ta'sir qilishi mumkin.
Haqiqiy dunyo misollari va foydalanish holatlari
1-misol: Promises bilan asinxron rasm yuklash
function loadImage(url) {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => resolve(img);
img.onerror = () => reject(new Error(`${url} manzilidagi rasmni yuklashda xatolik`));
img.src = url;
});
}
// Misol foydalanish:
loadImage('https://example.com/image.jpg')
.then(img => {
// Rasm muvaffaqiyatli yuklandi. DOMni yangilang.
document.body.appendChild(img);
})
.catch(error => {
// Rasm yuklash xatosini boshqaring.
console.error(error);
});
Ushbu misolda loadImage funksiyasi rasm muvaffaqiyatli yuklanganda hal qilinadigan yoki xato bo'lsa rad etadigan Promiseni qaytaradi. .then() va .catch() callbacklari Mikro-vazifalar navbatiga qo'shiladi va rasm yuklash operatsiyasi tugallangandan so'ng DOM yangilanishi va xatolarni boshqarish tezda bajarilishini ta'minlaydi.
2-misol: Dinamik UI yangilanishlari uchun MutationObserverdan foydalanish
const observer = new MutationObserver(mutations => {
mutations.forEach(mutation => {
console.log('Mutation kuzatildi:', mutation);
// Mutation asosida UI ni yangilang.
});
});
const elementToObserve = document.getElementById('myElement');
observer.observe(elementToObserve, {
attributes: true,
childList: true,
subtree: true
});
// Keyinroq, elementni o'zgartiring:
elementToObserve.textContent = 'Yangi kontent!';
MutationObserver DOMda o'zgarishlarni kuzatishga imkon beradi. Mutation sodir bo'lganda (masalan, atribut o'zgartirilganda, bolalar tuguni qo'shilganda), MutationObserver callbacki Mikro-vazifalar navbatiga qo'shiladi. Bu DOM o'zgarishlariga javoban UI tezda yangilanishini ta'minlaydi.
3-misol: Fetch API bilan tarmoq so'rovlarini boshqarish
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => {
console.log('Ma'lumotlar qabul qilindi:', data);
// Ma'lumotlarni qayta ishlang va UI ni yangilang.
})
.catch(error => {
console.error('Ma'lumotlarni olishda xatolik:', error);
// Xatoni boshqaring.
});
Fetch API JavaScriptda tarmoq so'rovlarini yuborishning zamonaviy usuli hisoblanadi. .then() callbacklari Mikro-vazifalar navbatiga qo'shiladi va javob olingandan so'ng ma'lumotlarni qayta ishlash va UI yangilanishlari bajarilishini ta'minlaydi.
Node.js Event Loopni ko'rib chiqish
Node.js dagi Event Loop brauzer muhitiga o'xshash ishlaydi, lekin ba'zi o'ziga xos xususiyatlarga ega. Node.js Event Loopning amalga oshirilishini, shuningdek asinxron I/O imkoniyatlarini ta'minlaydigan libuv kutubxonasidan foydalanadi.
process.nextTick(): Yuqorida aytib o'tilganidek, process.nextTick() - bu Node.js ga xos funksiya bo'lib, u joriy operatsiya tugallangandan so'ng, lekin Event Loop davom etmasdan oldin callbackni bajarishni rejalashtirishga imkon beradi. process.nextTick() bilan qo'shilgan callbacklar Mikro-vazifalar navbatidagi Promise callbacklaridan oldin bajariladi. Biroq, ochlik xavfi tufayli, process.nextTick() ni kamdan-kam ishlatish kerak. queueMicrotask() odatda mavjud bo'lganda afzalroqdir.
setImmediate(): setImmediate() funksiyasi Event Loopning keyingi iteratsiyasida callbackni bajarishni rejalashtiradi. Bu setTimeout(() => { ... }, 0) ga o'xshaydi, lekin setImmediate() I/O ga tegishli vazifalar uchun mo'ljallangan. setImmediate() va setTimeout(() => { ... }, 0) o'rtasidagi bajarilish tartibi oldindan aytib bo'lmaydi va tizimning I/O unumdorligiga bog'liq.
Event Loopni samarali boshqarish uchun eng yaxshi amaliyotlar
- Asosiy oqimni bloklashdan saqlaning. Uzoq muddatli sinxron operatsiyalar Event Loopni bloklashi mumkin va dasturni sezgir bo'lmay qoladi. Iloji boricha asinxron operatsiyalardan foydalaning.
- Kodingizni optimallashtiring. Samarali kod tezroq bajariladi, Qo'ng'iroqlar stekida o'tkazilgan vaqtni qisqartiradi va Event Loopga ko'proq vazifalarni qayta ishlashga imkon beradi.
- Asinxron operatsiyalar uchun Promisesdan foydalaning. Promises an'anaviy callbacklarga nisbatan asinxron kodni boshqarishning toza va qulayroq usulini taqdim etadi.
- Mikro-vazifalar navbatini hisobga oling. Ochlikka olib kelishi mumkin bo'lgan ortiqcha mikro-vazifalarni yaratishdan saqlaning.
- Hisoblash uchun og'ir vazifalar uchun Web Workersdan foydalaning. Web Workers JavaScript kodini alohida oqimlarda ishlatishga imkon beradi va asosiy oqimning bloklanishiga yo'l qo'ymaydi. (Brauzer muhitiga xos)
- Kodingizni profiling qiling. Unumdorlikning tor joylarini aniqlash va kodingizni optimallashtirish uchun brauzer ishlab chiquvchi vositalaridan yoki Node.js profiling vositalaridan foydalaning.
- Voqealarni cheklang va qisqartiring. Tez-tez ishlaydigan voqealar uchun (masalan, aylantirish voqealari, o'lchamini o'zgartirish voqealari) voqea ishlov beruvchisi bajariladigan vaqtlar sonini cheklash uchun cheklash yoki qisqartirishdan foydalaning. Bu Event Loopdagi yukni kamaytirish orqali unumdorlikni oshirishi mumkin.
Xulosa
JavaScript Event Loop, Vazifalar navbati va Mikro-vazifalar navbatini tushunish unumdor va javob beradigan JavaScript dasturlarini yozish uchun juda muhimdir. Event Loop qanday ishlashini tushunib, siz asinxron operatsiyalarni qanday boshqarish va kodingizni yaxshiroq unumdorlik uchun optimallashtirish haqida asosli qarorlar qabul qilishingiz mumkin. Mikro-vazifalarga tegishli ustuvorlik berishni, ochlikdan saqlanishni va har doim asosiy oqimni blokirovka operatsiyalaridan xoli qilishga intiling.
Ushbu qo'llanma JavaScript Event Loop haqida keng qamrovli ma'lumot berdi. Bu yerda ko'rsatilgan bilimlarni va eng yaxshi amaliyotlarni qo'llash orqali siz ajoyib foydalanuvchi tajribasini taqdim etadigan mustahkam va samarali JavaScript dasturlarini yaratishingiz mumkin.