Event Loop — Call Stack, Task Queue, Microtask Queue

~25 daq · JavaScript · Asinxron JavaScript

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