Mundarija (33)
- Bu darsda
- 1. Nega bu kerak?
- 2. Event loop'ning qismlari
- 2.1 To'rtta joy
- 2.2 Event loop — bitta qoida
- 3. queueMicrotask: birinchi microtask
- 3.1 Microtask qo'yish
- 3.2 Klassik savol
- 4. Microtask navbati to'liq bo'shatiladi
- 4.1 Microtask ichidan microtask
- 4.2 Microtask'lar taymerni "och qoldiradi"
- 4.3 Tartibni oldindan aytish: to'rt qadam
- 4.4 var li sikl siri
- 5. Chizish qadami
- 5.1 Brauzer qachon chizadi?
- 5.2 requestAnimationFrame haqida bir gap
- 6. Node'da event loop
- 6.1 Umumiy qism bir xil
- 6.2 setImmediate va fazalar
- 6.3 process.nextTick
- 7. Loupe: event loop'ni jonli ko'ring
- 8. Ko'p uchraydigan xatolar
- 8.1 Tartibni kod tartibidan taxmin qilish
- 8.2 setTimeout(fn, 0) va queueMicrotask(fn) ni bir xil deb o'ylash
- 8.3 Cheksiz microtask
- 8.4 Og'ir ishni bitta setTimeout ga o'rash
- 9. Mashqlar
- 1-mashq (oson): S, M yoki T?
- 2-mashq (o'rta): Taymer ichida microtask
- 3-mashq (qiyin): Og'ir ishni bo'laklash
- 10. Real ishda
- Xulosa
- Manbalar
JavaScript event loop: stek, task va microtask navbatlari
Qisqacha: Event loop — call stack bo'shashini kutib, navbatdagi ishni stekka beradigan mexanizm. Bitta aylanishda: navbatdan bitta task (taymer, bosish) bajariladi, keyin microtask navbati to'liq bo'shatiladi, keyin brauzer kerak bo'lsa ekranni chizadi. Shuning uchun
queueMicrotaskcallback'isetTimeout(fn, 0)dan oldin ishlaydi:1, 2(sinxron) →3(microtask) →4(taymer).
Bu darsda
- Event loop'ning qismlarini — call stack, Web API, task navbati, microtask navbati — va ular orasidagi yo'lni chiza olasiz.
- Taymer, microtask va sinxron kod aralash kodning chiqish tartibini oldindan, qadam-baqadam ayta olasiz.
- Brauzer ekranni qachon chizishini va nega uzun ish paytida chizmasligini tushuntira olasiz.
- Microtask navbati nega taymerlarni "och qoldirishi" mumkinligini bilasiz.
- Node'ning event loop'i brauzernikidan nimasi bilan farq qilishini (
setImmediate,process.nextTick) bilasiz.
Oldin bilishingiz kerak: Execution context va call stack, Sinxron va asinxron kod, Taymerlar, Callbacklar va callback hell.
1. Nega bu kerak?
Oxirgi darslarda bir nechta "sir" yig'ildi:
setTimeout(fn, 0)— 0 ms bo'lsa ham, joriy kod tugagandan keyin ishlaydi (Taymerlar).- 100 ms li taymer stek band bo'lgani uchun 1000 ms da ishladi (Sinxron va asinxron kod).
- Sinxron kutish paytida "Kutilmoqda..." yozuvi ekranga umuman chiqmadi.
- Closure tuzoqlari darsida
varli sikldagi taymerlar uchala marta4-urinishdeb yozdi — "nega shunday, Event loop darsida ko'ramiz" degan edik.
Bularning hammasi bitta mexanizmdan kelib chiqadi. Bugun shu mexanizmni to'liq ochamiz. Undan keyin kodga qarab, natijalar qaysi tartibda chiqishini ishga tushirmasdan ayta olasiz — bu asinxron JavaScript'dagi eng muhim ko'nikma va intervyularning eng sevimli savoli.
«Bahor» oshxonasini davom ettiramiz. Execution context darsida Rustam aka ilgichdagi eng ustki varaq bo'yicha ishlardi — bu call stack edi. Endi oshxonaning qolgan qismini ko'ramiz: buyurtmalar qayerdan keladi va kim ularni ilgichga qo'yadi?
2. Event loop'ning qismlari
2.1 To'rtta joy
Asinxron kodda har bir callback to'rt joydan o'tishi mumkin:
| Joy | Nima | «Bahor»da |
|---|---|---|
| Call stack | hozir bajarilayotgan kod | Rustam aka qo'lidagi varaq |
| Web API (host) | kutayotgan ishlar: taymer, tarmoq, hodisa | qozonlar, qum soatlari, eshik qo'ng'irog'i |
| Task navbati | vaqti kelgan, stekni kutayotgan callback'lar | kassadagi buyurtmalar navbati |
| Microtask navbati | "joriy ish tugashi bilan" bajarilishi kerak bo'lgan kichik ishlar | joriy taomga qo'shiladigan "yana bir narsa" ro'yxati |
Uchtasi tanish. Call stack — Execution context dan. Web API va navbat — Sinxron va asinxron kod dagi vizualdan. Yangi ikkitasini aniqlaymiz.
Task (macrotask deb ham ataladi) — event loop bir aylanishda stekka beradigan bitta mustaqil ish: skriptning o'zi, taymer callback'i, bosish hodisasi tinglovchisi, tarmoqdan kelgan javob. Task navbati (task queue) — shunday ishlar kelish tartibida turadigan navbat. Navbat (queue) — birinchi kelgan birinchi ketadi, xuddi kassa oldidagi odamlar kabi.
Microtask — joriy task tugashi bilan, keyingi task'dan oldin bajarilishi shart bo'lgan kichik ish. Microtask navbati (microtask queue) — ular uchun alohida, "imtiyozli" navbat.
2.2 Event loop — bitta qoida
Event loop (hodisalar sikli) — host ichidagi cheksiz takrorlanuvchi jarayon. U doim bitta savolni beradi: "stek bo'shmi? Bo'sh bo'lsa — navbatdan keyingisini ber".
Aniqroq, bitta aylanish shunday (brauzer uchun):
- Task navbatidan bitta task olinadi va bajariladi — stek bo'shaguncha.
- Microtask navbati to'liq bo'shatiladi — hatto shu orada yangi microtask qo'shilsa ham.
- Vaqti kelgan bo'lsa, brauzer ekranni chizadi (render).
- 1-qadamga qaytiladi. Navbat bo'sh bo'lsa — yangi task kelishini kutadi.
Sxemada qarang. Strelkalar bitta aylanishni ko'rsatadi:
flowchart TD
T["Navbatdan<br/>BITTA task"] --> S["Stekda bajarish<br/>(bo'shaguncha)"]
S --> M{"Microtask<br/>navbati bo'shmi?"}
M -- "yo'q" --> MB["Bitta microtask<br/>bajarish"]
MB --> M
M -- "ha" --> R{"Chizish<br/>vaqti keldimi?"}
R -- "ha" --> C["Ekranni chizish"]
R -- "yo'q" --> T
C --> TE'tibor bering: task'dan faqat bitta, microtask'dan esa hammasi. Bu farq bugungi hamma tartib savollarining kaliti.
Skriptning o'zi ham — birinchi task. Shuning uchun sinxron kod har doim birinchi va to'liq bajariladi: u tugamaguncha stek bo'shamaydi, event loop esa hech narsa bera olmaydi.
Tekshirib ko'ring: Event loop stekda biror funksiya bajarilayotganda uni to'xtatib, navbatdagi taymer callback'ini bera oladimi?
Javob
Yo'q. Event loop faqat stek bo'sh bo'lganda navbatdagini beradi. Bajarilayotgan kodni hech kim to'xtatmaydi — u oxirigacha ishlaydi. Shuning uchun uzun sinxron kod hamma taymer va bosishlarni kechiktiradi.
3. queueMicrotask: birinchi microtask
3.1 Microtask qo'yish
Microtask navbatiga ish qo'yishning eng to'g'ridan-to'g'ri yo'li — queueMicrotask(fn). U fn ni microtask navbatiga qo'yadi va darhol qaytadi. Brauzerda ham, Node'da ham bor.
Microtask'ning asosiy manbai esa boshqa — Promise. Promise natijasini kutadigan callback'lar aynan microtask navbatiga tushadi. Uni keyingi darsda, Promise asoslari da boshlaymiz — hozir bilish shart emas. Bugun bitta queueMicrotask bilan mexanizmni tushunsangiz, Promise tartibi ham o'z-o'zidan ochiladi.
3.2 Klassik savol
Kod, taymer va microtask aralash:
console.log("1. Skript boshlandi");
setTimeout(() => {
console.log("4. Taymer");
}, 0);
queueMicrotask(() => {
console.log("3. Microtask");
});
console.log("2. Skript tugadi");Konsolda:
1. Skript boshlandi
2. Skript tugadi
3. Microtask
4. TaymerTaymer kodda microtask'dan oldin yozilgan va 0 ms. Baribir microtask oldin chiqdi. Qadamma-qadam qarang. O'ng tomonda uch navbat-joy: Web API, Task navbati va Microtask navbati:
Qoidani bir gapda: sinxron kod → hamma microtask'lar → bitta task → hamma microtask'lar → bitta task ...
Tekshirib ko'ring: Kodda ikkita
setTimeout(fn, 0)va ulardan keyin bittaqueueMicrotaskbo'lsa, microtask nechanchi bo'lib ishlaydi (sinxron koddan keyin)?
Javob
Birinchi. Ikkala taymer callback'i task navbatida, microtask esa microtask navbatida. Skript tugashi bilan avval microtask navbati bo'shatiladi, keyin taymerlar birin-ketin — har biri alohida aylanishda.
4. Microtask navbati to'liq bo'shatiladi
4.1 Microtask ichidan microtask
Event loop qoidasining ikkinchi qadamini eslang: "microtask navbati to'liq bo'shatiladi — hatto shu orada yangisi qo'shilsa ham". Buni sinaymiz. Microtask ichidan yana microtask va yana taymer qo'yamiz. Ishga tushirishdan oldin tartibni o'zingiz ayting:
console.log("1");
setTimeout(() => console.log("2: taymer"), 0);
queueMicrotask(() => {
console.log("3: microtask");
queueMicrotask(() => console.log("4: microtask ichidan"));
setTimeout(() => console.log("5: microtask ichidan taymer"), 0);
});
console.log("6");Konsolda:
1
6
3: microtask
4: microtask ichidan
2: taymer
5: microtask ichidan taymerQadamlarda kuzating — ayniqsa "4" ning navbatga qachon qo'shilib, qachon chiqishiga:
Ikki xulosa:
- «4» — microtask ichida qo'yilgan bo'lsa ham — taymer «2» dan oldin chiqdi. Microtask navbati bo'shamaguncha task'ga navbat kelmaydi.
- «5» — «2» dan keyin. Ikkalasi task navbatida, «5» keyin qo'yilgan. Navbat — birinchi kelgan birinchi ketadi.
4.2 Microtask'lar taymerni "och qoldiradi"
Microtask o'zidan keyin yana microtask qo'yaversa nima bo'ladi? Taymer kutaveradi:
setTimeout(() => {
console.log("Taymer keldi");
}, 0);
let soni = 0;
function kichikIsh() {
soni++;
if (soni < 1_000_000) {
queueMicrotask(kichikIsh);
} else {
console.log("Microtask'lar tugadi:", soni);
}
}
queueMicrotask(kichikIsh);
console.log("Sinxron kod tugadi");Konsolda:
Sinxron kod tugadi
Microtask'lar tugadi: 1000000
Taymer keldi0 ms li taymer million microtask tugashini kutdi. Agar if sharti bo'lmasa — microtask'lar abadiy bir-birini qo'yaverardi. Taymer hech qachon ishlamasdi, brauzerda esa sahifa butunlay qotardi: chizish ham microtask'lardan keyin keladi.
Taqqoslang: xuddi shunday cheksiz zanjirni setTimeout(kichikIsh, 0) bilan qilsangiz, sahifa qotmaydi. Har taymer — alohida task. Ularning orasida event loop bosishlarni ham, chizishni ham bajarishga ulguradi. Bu — microtask va task'ning eng amaliy farqi.
Diqqat: Microtask — "tezroq setTimeout" emas. U joriy task'ning davomi: hammasi tugaguncha brauzer hech narsa qila olmaydi. Uzun ishni microtask'larga bo'lish sahifani qutqarmaydi — buning uchun task'lar (taymer) kerak.
4.3 Tartibni oldindan aytish: to'rt qadam
Endi har qanday aralash kod uchun tartibni qog'ozda topa olasiz. Har safar bir xil yo'l bilan:
- Sinxron qatorlarni belgilang — ular kod tartibida, hammasidan oldin chiqadi.
- Har callback qaysi navbatga tushishini yozing:
queueMicrotask— microtask,setTimeout/setInterval— task (vaqti kelganda), hodisa — task (sodir bo'lganda). - Skript tugagach — microtask navbatini oxirigacha "bajaring". Ular ichida yangi callback'lar qo'yilsa, ularni ham tegishli navbatning oxiriga yozing.
- Task navbatidan bittasini oling, bajaring, yana 3-qadamga qayting.
Kechikishi har xil taymerlar task navbatiga vaqti kelganda tushadi: 100 ms li taymer 0 ms lidan keyin, qaysi biri oldin yozilganidan qat'i nazar.
4.4 var li sikl siri
Endi Closure tuzoqlari darsidagi va'dani bajaramiz. U yerdagi kod:
for (var urinish = 1; urinish <= 3; urinish++) {
setTimeout(() => console.log(`${urinish}-urinish`), 0);
}Konsolda:
4-urinish
4-urinish
4-urinishTo'rt qadam bilan yechamiz. Sikl — sinxron kod: u uchta taymer qo'yadi va tugaydi. Sikl tugaganda urinish — 4 (shart 4 <= 3 bajarilmagani uchun to'xtadi). Uchala callback task navbatida kutadi va faqat skript tugagandan keyin ishlaydi. Ular ishlaganda var urinish — bitta umumiy o'zgaruvchi — allaqachon 4. let bilan esa har aylanishda yangi o'zgaruvchi bo'ladi va callback'lar 1, 2, 3 ni ko'radi. Closure qaysi o'zgaruvchini eslashini — Closure darslari, qachon ishlashini esa event loop tushuntiradi.
5. Chizish qadami
5.1 Brauzer qachon chizadi?
Chizish (render) — brauzer DOM o'zgarishlarini hisoblab, ekranga piksellar sifatida chiqarishi. Ekran odatda soniyada 60 marta yangilanadi — har 16,7 ms da bir imkoniyat. Event loop bu imkoniyatni faqat task va uning microtask'lari tugagandan keyin ishlatadi.
Demak siz bitta tinglovchida DOM'ni o'n marta o'zgartirsangiz, foydalanuvchi faqat oxirgisini ko'radi. Sinxron va asinxron kod darsidagi "Kutilmoqda..." shuning uchun ko'rinmagan edi.
Ikki tugmani sinang. Ikkalasi ham 1 dan 5 gacha sanaydi va har raqamda 300 ms ish qiladi. «Bitta task» hammasini bitta tinglovchida bajaradi. «Har qadam — alohida task» har raqamni alohida setTimeout da bajaradi:
<style>
body { font: 1rem/1.5 system-ui, sans-serif; margin: 1rem; }
.raqam { font: 700 3rem/1.2 ui-monospace, monospace; margin: 0; }
button { font: inherit; padding: 0.4rem 0.8rem; margin: 0.25rem 0; }
</style>
<p class="raqam" id="raqam">0</p>
<button id="bitta" type="button">Bitta task</button>
<button id="alohida" type="button">Har qadam — alohida task</button>
<script>
const raqam = document.getElementById("raqam");
function bandQil(ms) {
const boshi = Date.now();
while (Date.now() - boshi < ms) {}
}
document.getElementById("bitta").addEventListener("click", () => {
for (let i = 1; i <= 5; i++) {
raqam.textContent = i;
bandQil(300);
}
console.log("Bitta task: tugadi");
});
function qadam(i) {
raqam.textContent = i;
bandQil(300);
if (i < 5) {
setTimeout(qadam, 0, i + 1);
} else {
console.log("Alohida task'lar: tugadi");
}
}
document.getElementById("alohida").addEventListener("click", () => {
qadam(1);
});
</script>Nimani ko'rdingiz:
- «Bitta task» — 1,5 soniya hech narsa o'zgarmaydi, keyin birdan 5 chiqadi. 1, 2, 3, 4 ham
textContentga yozildi, lekin chizilmadi: stek bo'shamadi. - «Har qadam — alohida task» — raqamlar birin-ketin almashadi. Har
qadamalohida task, ular orasida brauzer chizishga ulguradi.
Ikkala holatda ham umumiy ish bir xil — 1,5 soniya. Lekin ikkinchisida foydalanuvchi jarayonni ko'radi va sahifa har 300 ms da "nafas oladi" — bosishlar ham shu oraliqlarda bajariladi. Og'ir ishni task'larga bo'laklash — bloklashga qarshi asosiy usullardan biri. Uning to'liq ko'rinishini 3-mashqda yozasiz.
5.2 requestAnimationFrame haqida bir gap
Chizishdan oldin, aynan har kadr uchun ishlaydigan callback ham bor — requestAnimationFrame. U animatsiya uchun mo'ljallangan va 13-qismda, requestAnimationFrame darsida o'rganiladi — hozir bilish shart emas. Shuni biling: u ham event loop'ning bir qismi, chizish qadamiga biriktirilgan.
Tekshirib ko'ring: Tinglovchi DOM'ga 1000 ta element qo'shdi, keyin
queueMicrotaskbilan yana bittasini. Foydalanuvchi ekranda avval 1000 tasini, keyin 1001 tasini ko'radimi?
Javob
Yo'q — darhol 1001 tasini ko'radi. Microtask task tugashi bilan, chizishdan oldin bajariladi. Brauzer birinchi marta chizganda DOM'da allaqachon 1001 ta element bor.
6. Node'da event loop
6.1 Umumiy qism bir xil
Node'da ham call stack bitta, ham task'lar va microtask'lar bor, ham "microtask navbati to'liq bo'shatiladi" qoidasi ishlaydi. Shu darsdagi js misollar Node 24 da tekshirilgan va brauzerda ham xuddi shu tartibni beradi. Farqlar — task'larning ichki tartibida va Node'ning o'z qo'shimcha navbatlarida. Ekranni chizish qadami esa Node'da yo'q — ekran yo'q.
6.2 setImmediate va fazalar
Node'ning event loop'i bitta task navbati emas, bir nechta faza (bosqich) dan iborat: taymerlar, kirish-chiqish — inglizcha I/O (input/output: fayl, tarmoq) — callback'lari, setImmediate va boshqalar. Har aylanishda fazalar ma'lum tartibda o'tiladi. Fazalarni Event loop fazalari chuqur darsida, 24-qismda ko'ramiz.
setImmediate(fn) — faqat Node'da: "kirish-chiqish fazasidan keyin, darhol". Fayl o'qilgandan keyin taymer va setImmediate qo'ysak, setImmediate doim birinchi:
import { readFile } from "node:fs";
readFile(import.meta.filename, () => {
setTimeout(() => console.log("setTimeout 0"), 0);
setImmediate(() => console.log("setImmediate"));
});Konsolda:
setImmediate
setTimeout 0import.meta.filename — shu faylning o'z yo'li: fayl o'zini o'qiydi, shunchaki o'qiladigan biror fayl kerak edi. Fayl callback'idan keyin event loop setImmediate fazasiga keladi, taymerlar fazasi esa keyingi aylanishda. Faylning yuqori darajasida esa bu ikkisining tartibi kafolatlanmaydi — kompyuter tezligiga qarab o'zgaradi. Bunday tartibga tayanmang.
6.3 process.nextTick
process.nextTick(fn) — Node'ning yana bir navbati, microtask'larga o'xshash, lekin alohida. Oddiy .cjs faylda u queueMicrotask dan ham oldin ishlaydi. ESM (.mjs) faylning yuqori darajasida esa — keyin. Bu nozik farq va uning sababi — Microtask navbati, process.nextTick va setImmediate darsida. Bugun bitta qoida: yangi kodda queueMicrotask ni tanlang — u brauzerda ham, Node'da ham bir xil ishlaydi.
| Brauzer | Node | |
|---|---|---|
| Task'lar | bitta (asosiy) navbat | fazalar: taymerlar, I/O, setImmediate ... |
| Microtask | queueMicrotask, Promise |
queueMicrotask, Promise + process.nextTick |
| Chizish | bor | yo'q |
setTimeout(fn, 0) |
0 ms, ichma-ich — kamida 4 ms | 1 ms |
Tekshirib ko'ring: Node'da faylning eng yuqorisida (hech qanday callback ichida emas)
setTimeout(fn, 0)vasetImmediate(fn)yozildi. Qaysi biri birinchi ishlaydi?
Javob
Oldindan aytib bo'lmaydi — tartib kafolatlanmagan. U event loop qaysi fazadan boshlashiga va kompyuter tezligiga bog'liq. setImmediate doim birinchi bo'lishi faqat kirish-chiqish (I/O) callback'i ichida, masalan readFile callback'ida kafolatlangan.
7. Loupe: event loop'ni jonli ko'ring
Philip Roberts 2014 yilda event loop haqida mashhur chiqish qilgan ("What the heck is the event loop anyway?", JSConf EU) va uning uchun vizualizator yozgan — Loupe: latentflip.com/loupe. Unga kod yozasiz, u esa stek, Web API'lar va task navbati orasidagi harakatni sekinlashtirib ko'rsatadi. Tugma bosish hodisalarini ham sinash mumkin.
Ikki cheklovini biling: Loupe eski vosita — microtask navbatini ko'rsatmaydi, zamonaviy sintaksis bilan esa ba'zan ishlamaydi. Xato bersa, arrow funksiyalar o'rniga function yozing. Bu darsdagi birinchi vizualning queueMicrotask siz qismini Loupe'da ishga tushirib, taymer callback'i yo'lini kuzating.
8. Ko'p uchraydigan xatolar
8.1 Tartibni kod tartibidan taxmin qilish
"setTimeout yuqorida yozilgan — demak birinchi chiqadi". Yo'q: tartibni navbat turi belgilaydi. Avval sinxron kod, keyin microtask'lar, keyin task'lar — har biri o'z navbatida kelish tartibida. Tuzatish: murakkab kodda har callback yoniga "S", "M" yoki "T" deb belgi qo'yib, keyin tartiblang — 1-mashqda shunday qilasiz.
8.2 setTimeout(fn, 0) va queueMicrotask(fn) ni bir xil deb o'ylash
Ikkalasi ham "joriy koddan keyin". Lekin microtask — shu task'ning oxirida, chizishdan oldin; taymer — keyingi task'larning birida, ehtimol chizishdan keyin. Foydalanuvchi oraliq holatni ko'rishi kerak bo'lsa — taymer. Hech qanday oraliq holat ko'rinmasligi kerak bo'lsa — microtask.
8.3 Cheksiz microtask
O'zini qayta qo'yadigan microtask — cheksiz sikl bilan bir xil: taymerlar, bosishlar va chizish abadiy kutadi. Brauzer bir necha soniyadan keyin «Page Unresponsive» oynasini chiqaradi. Tuzatish: takrorlanuvchi ish uchun taymer ishlating yoki to'xtash shartini tekshiring.
8.4 Og'ir ishni bitta setTimeout ga o'rash
setTimeout(ogirHisob, 0) — sahifani qutqarmaydi, faqat qotishni bir lahzaga kechiktiradi. Task baribir 2 soniya stekni egallaydi. Tuzatish: ishni bo'laklarga bo'ling — har bo'lak alohida task (3-mashq).
9. Mashqlar
1-mashq (oson): S, M yoki T?
Har console.log yoniga "S" (sinxron), "M" (microtask) yoki "T" (taymer task'i) deb belgi qo'ying va chiqish tartibini yozing. Keyin tekshiring.
setTimeout(() => console.log("A"), 0);
console.log("B");
queueMicrotask(() => console.log("C"));
setTimeout(() => console.log("D"), 0);
queueMicrotask(() => console.log("E"));
console.log("F");Yechim
A — T, B — S, C — M, D — T, E — M, F — S.
Konsolda:
B
F
C
E
A
DAvval sinxronlar (B, F) kod tartibida. Keyin microtask navbati to'liq: C, E. Keyin task'lar — har biri alohida aylanishda, qo'yilgan tartibda: A, D.
2-mashq (o'rta): Taymer ichida microtask
Tartibni oldindan ayting. Ishora: har taymer callback'i — alohida task, va har task'dan keyin microtask navbati bo'shatiladi.
setTimeout(() => {
console.log("1-taymer");
queueMicrotask(() => console.log("1-taymerning microtask'i"));
}, 0);
setTimeout(() => {
console.log("2-taymer");
}, 0);
queueMicrotask(() => {
console.log("microtask");
setTimeout(() => console.log("3-taymer"), 0);
});Yechim
Konsolda:
microtask
1-taymer
1-taymerning microtask'i
2-taymer
3-taymerSinxron kod hech narsa chiqarmaydi, faqat navbatlarga qo'yadi. Skript tugagach — microtask: u "3-taymer" ni task navbatining oxiriga qo'shadi. Keyin 1-taymer — uning microtask'i shu task tugashi bilan, 2-taymerdan oldin ishlaydi. Keyin 2-taymer, oxirida 3-taymer.
3-mashq (qiyin): Og'ir ishni bo'laklash
Rustam aka oy oxirida 3 000 000 ta chekning summasini hisoblamoqchi (har chek — i % 100 so'm, ya'ni 0 dan 99 gacha). Bitta siklda hisoblasa, shu orada kelgan buyurtma kutib qoladi. bolaklabHisobla(jami, bolak, tugadi) funksiyasini yozing: u har safar bolak ta chekni hisoblasin va keyingi bo'lakni setTimeout(..., 0) bilan rejalashtirsin. Oxirida tugadi(summa) chaqirilsin.
Sinov: jami = 3_000_000, bolak = 1_000_000. Hisob boshlanishidan oldin setTimeout(() => console.log("Yangi buyurtma qabul qilindi"), 0) qo'ying — u hisob oxirida emas, birinchi bo'lakdan keyin chiqishi kerak. Faylni kurs/mashqlar/11/04-event-loop/bolaklash.mjs ga saqlang.
Yechim
function bolaklabHisobla(jami, bolak, tugadi) {
let summa = 0;
let i = 0;
let tartib = 0;
function keyingiBolak() {
const chegara = Math.min(i + bolak, jami);
for (; i < chegara; i++) {
summa += i % 100;
}
tartib++;
console.log(`${tartib}-bo'lak tugadi`);
if (i < jami) {
setTimeout(keyingiBolak, 0);
} else {
tugadi(summa);
}
}
keyingiBolak();
}
setTimeout(() => console.log("Yangi buyurtma qabul qilindi"), 0);
bolaklabHisobla(3_000_000, 1_000_000, (summa) => {
console.log("Jami summa:", summa);
});Konsolda:
1-bo'lak tugadi
Yangi buyurtma qabul qilindi
2-bo'lak tugadi
3-bo'lak tugadi
Jami summa: 148500000Birinchi bo'lak sinxron bajarildi (skript task'ining ichida). Keyin task navbatida ikki ish bor edi: avval qo'yilgan "Yangi buyurtma" va keyin qo'yilgan 2-bo'lak. Navbat tartibi bo'yicha buyurtma birinchi o'tdi. Bitta siklda hisoblaganda esa u eng oxirida chiqardi.
for (; i < chegara; i++) — for ning birinchi qismi bo'sh: i sikldan tashqarida e'lon qilingan, chunki u bo'laklar orasida saqlanishi kerak (closure). Summani tekshirish: har 100 ta chekda 0+1+...+99 = 4950, 3 000 000 da esa 30 000 × 4950 = 148 500 000.
git add 11/04-event-loop/bolaklash.mjs
git commit -m "11/04: og'ir hisobni bo'laklarga bo'l"10. Real ishda
- Intervyu. "Bu kod nimani qaysi tartibda chiqaradi?" — Promise,
setTimeoutvaasyncaralash kod bilan — frontend va Node intervyularining eng ko'p uchraydigan amaliy savoli. Bugungi qoida ("sinxron → hamma microtask → bitta task") Promise bilan ham aynan shunday ishlaydi. - Sezgir interfeys. React, Vue kabi freymvorklar DOM yangilanishlarini bitta microtask yoki kadrga yig'ib, bir marta chizadi — ular bugungi chizish qoidasiga tayanadi. React 17-qismda; hozir bilish shart emas.
- Og'ir ish. Katta jadvallarni filtrlash, fayllarni tahlil qilish — bo'laklash yoki alohida thread (Web Workers). Brauzerlarda bo'laklash uchun maxsus
scheduler.yield()ham paydo bo'lmoqda — 13-qismda. - Node serverlar. Bitta so'rov uchun og'ir sinxron hisob — hamma foydalanuvchilar uchun to'xtash. Node'chilar buni "event loop'ni bloklama" deb aytishadi; event loop kechikishini o'lchaydigan monitoring vositalari bor (Blocking va non-blocking I/O).
Xulosa
- Event loop — stek bo'shaganda navbatdagini beradigan mexanizm; ishlayotgan kodni hech qachon to'xtatmaydi.
- Bir aylanish: bitta task → hamma microtask'lar (yangi qo'shilganlari ham) → kerak bo'lsa chizish.
- Task'lar: skript, taymerlar, hodisalar, tarmoq javoblari. Microtask'lar:
queueMicrotask, Promise callback'lari. - Tartib qoidasi: sinxron kod → microtask'lar → task'lar, har navbat ichida — kelish tartibida.
- Brauzer faqat task va microtask'lar tugagach chizadi; cheksiz microtask sahifani butunlay qotiradi.
- Node'da qo'shimcha: fazalar,
setImmediate,process.nextTick; chizish yo'q.
Keyingi dars: Promise asoslari — callback'lar o'rniga "kelajakdagi natija" obyekti: kutilmoqda, bajarildi va rad etildi holatlari, then va catch — va ular nega microtask navbatidan foydalanadi.
Manbalar
- HTML Living Standard: "Event loops" (processing model, microtask checkpoint, update the rendering) — html.spec.whatwg.org
- MDN: "Using microtasks in JavaScript with queueMicrotask()", "The event loop" — developer.mozilla.org
- Jake Archibald, "Tasks, microtasks, queues and schedules" — jakearchibald.com
- Node.js: "The Node.js Event Loop" — nodejs.org/learn
- Philip Roberts, "What the heck is the event loop anyway?" (JSConf EU 2014) va Loupe — latentflip.com/loupe
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!