Event Loop — Call Stack, Task Queue, Microtask Queue
Muammodan boshlaylik
console.log("1");
setTimeout(() => console.log("2"), 0);
Promise.resolve().then(() => console.log("3"));
console.log("4");
Bu kodni o'qib, natijani taxmin qiling. Ko'pchilik "1, 2, 3, 4" deydi. Aslida:
1
4
3
2
setTimeout(..., 0) — nol millisoniya. "Darhol" degan ma'no. Lekin 3 dan keyin chiqdi. Nima uchun 2 eng oxirda?
Bu savolga javob — Event Loop. Uni tushunmasdan JavaScript ning asinxron ishlashini haqiqiy ma'noda tushunib bo'lmaydi.
Call Stack — hozir ishlayotgan joy
JavaScript kodi ishlayotganda bir "ustun" bor — Call Stack (chaqiruv ustuni). Funksiya chaqirilganda u ustunning tepasiga qo'yiladi. Funksiya tugaganda ustundan chiqariladi.
Hayotiy o'xshatma
Restoran oshpazi va taomlar to'plami. Oshpaz bir vaqtda bitta taomni pishiradi. Yangi buyurtma kelsa — ustiga qo'yiladi, bitirilganda olinadi. Oxirgi qo'yilgan — birinchi bitadilib olinadi.
function uchinchi() { console.log("uchinchi"); }
function ikkinchi() { uchinchi(); }
function birinchi() { ikkinchi(); }
birinchi();
Stack shunday o'zgaradi:
birinchi() ← qo'shildi
ikkinchi() ← qo'shildi
uchinchi() ← qo'shildi → ishladi → olib tashlandi
ikkinchi() ← ishladi → olib tashlandi
birinchi() ← ishladi → olib tashlandi
Stack bo'sh ← hammasi tugadi
Stack bo'sh bo'lmaguncha — JavaScript boshqa hech narsaga o'tolmaydi.
Asinxron ishlar qayerga boradi?
setTimeout, fetch, DOM hodisalari — bular JavaScript ning o'zida bajarilmaydi. Ular brauzerga topshiriladi. Brauzer bu ishlarni parallel bajaradi (o'z iplarida).
Ish tugagach, brauzer "natija tayyor" deb JavaScript ga xabar bermoqchi. Lekin Stack band bo'lishi mumkin. Shu sababli natija navbatga qo'yiladi — Stack bo'shaganda navbatdan olinadi.
Bu navbatlar ikkita: Microtask Queue va Macrotask Queue.
Ikki navbat — ikkita ustuvorlik
Macrotask Queue
setTimeout, setInterval, DOM hodisalari (click, input) — bular Macrotask Queue ga boradi.
Microtask Queue
Promise.then, Promise.catch, queueMicrotask — bular Microtask Queue ga boradi.
Farqi nima?
Stack bo'shaganda Event Loop quyidagicha ishlaydi:
1. Stack bo'shmi? → tekshir
2. Bo'sh bo'lsa → Microtask Queue ni tekshir
3. Microtask bor? → HAMMASINI bajargandan keyin
4. Macrotask Queue dan BITTASINI ol → bajargandan keyin
5. Yana 2-qadamdan
Microtask — yuqori ustuvorlik. Hech bo'lmasa bittasi qolmaguncha Macrotask boshlanmaydi.
Boshidagi misol — qadam-baqadam
console.log("1"); // sinxron
setTimeout(() => console.log("2"), 0); // Macrotask Queue ga
Promise.resolve().then(() => console.log("3")); // Microtask Queue ga
console.log("4"); // sinxron
Nima bo'ldi:
Stack: console.log("1") → ishladi → "1" chiqdi → Stack bo'shadi
Stack: setTimeout(...) → brauzerga topshirildi → callback Macrotask Queue ga
Stack: Promise.then(...) → Microtask Queue ga
Stack: console.log("4") → ishladi → "4" chiqdi → Stack bo'shadi
Stack endi bo'sh → Event Loop ishga kiradi
Microtask Queue tekshirildi → () => log("3") bor → ishladi → "3" chiqdi
Microtask Queue bo'shadi
Macrotask Queue tekshirildi → () => log("2") bor → ishladi → "2" chiqdi
Natija: 1, 4, 3, 2
Vizual
┌─────────────────────────────┐
│ CALL STACK │
│ hozir ishlayotgan kod │
└──────────────┬──────────────┘
│ bo'sh bo'lganda
▼
┌─────────────────────────────┐
│ MICROTASK QUEUE │ ← yuqori ustuvorlik
│ Promise.then, catch │
│ (hammasi bajariladi) │
└──────────────┬──────────────┘
│ bo'sh bo'lganda
▼
┌─────────────────────────────┐
│ MACROTASK QUEUE │ ← past ustuvorlik
│ setTimeout, setInterval │
│ DOM hodisalar │
│ (bittadan bajariladi) │
└─────────────────────────────┘
Nima uchun bu farq muhim?
Agar Microtask va Macrotask bir xil ustuvorlikda bo'lganda — Promise.then va setTimeout(fn, 0) tartibsiz ishlardi. Dasturchilar ularning tartibiga tayanolmasdi.
Hozirgi qoida qat'iy: Promise zanjiri — har doim setTimeout dan oldin ishlaydi. Bu kafolat ustida kod yozish mumkin.
Stack overflow
Stack cheksiz emas. Funksiya o'z ichida o'zini chaqirsa va to'xtamasa — Stack to'lib xato beradi:
function cheksiz() {
return cheksiz();
}
cheksiz(); // ❌ RangeError: Maximum call stack size exceeded
Bu xato rekursiyada chiqish sharti bo'lmasa yuzaga keladi. Stack taxminan 10,000–15,000 daraja chuqurlikni ko'tara oladi.
Event Loop ni to'sib qo'yish
Stack band bo'lsa — Event Loop kutadi. Demak Microtask va Macrotask ham kutadi. Uzoq sinxron ish — butun sahifani muzlatadi.
// Bu 5 soniya davomida sahifani muzlatadi
button.addEventListener("click", () => {
const boshi = Date.now();
while (Date.now() - boshi < 5000) {}
console.log("tugadi");
});
Bu 5 soniyada: scroll ishlamaydi, animatsiyalar to'xtaydi, boshqa tugmalar javob bermaydi. Brauzer sahifani "Not Responding" deb belgilashi mumkin.
Katta hisoblashlarda — ishni bo'laklarga bo'lib, setTimeout(fn, 0) orqali Event Loop ga nafas olish imkoni berish kerak.
Tez-tez uchraydigan xatolar
Microtask ichida yana Microtask — to'xtamaydigan zanjir:
function tinmaydi() {
Promise.resolve().then(tinmaydi); // ← har Microtask yangi Microtask qo'shadi
}
tinmaydi(); // ❌ — Macrotask hech qachon bajarilmaydi, sahifa muzlaydi
Microtask Queue to'liq bo'shagunicha Macrotask boshlanmaydi. Cheksiz Microtask zanjiri — sahifani muzlatadi.
setTimeout 0 ni "darhol" deb tushunish:
setTimeout(() => console.log("A"), 0);
console.log("B");
// Natija: B, A
// "0ms" = "Stack bo'shagandan keyin" degani, haqiqatda "darhol" emas
setTimeout(fn, 0) — "imkon bo'lganda, lekin hozirgi sinxron kod tugagandan keyin" degan ma'no.
Xulosa
Call Stack — hozir ishlayotgan kod. Funksiyalar ustma-ust qo'yiladi, tugaganda olinadi.
Microtask Queue — Promise natijalari. Stack bo'shaganda — hammasi bajariladi.
Macrotask Queue — setTimeout, DOM hodisalar. Microtask Queue bo'shagandan keyin — bittadan bajariladi.
Event Loop — Stack ni, keyin Microtask ni, keyin Macrotask ni tekshirib turadi. Bu jarayon to'xtamaydi.
Keyingi dars
36-dars: Callback va Callback Hell