Event Loop nima — brauzer dan farqi, nima uchun muhim

~20 daq · Node.js · Event Loop

Muammodan boshlaylik

Siz server yozdingiz. Birinchi foydalanuvchi so'rov yubordi — server ma'lumotlar bazasidan ma'lumot olmoqda. Bu vaqt 200ms. Ikkinchi foydalanuvchi so'rov yubordi — server nima qiladi? Birinchi so'rov tugashini kutadimi? Yo'q. Ikkinchi so'rovni ham qabul qiladi va parallel ishlaydi.

Lekin JavaScript bitta thread da ishlaydi. Qanday qilib bir vaqtda ikki ishni bajaradi? Javob — Event Loop.

Bu Node.js ning eng muhim qismi. Buni tushunmasdan Node.js ni tushunib bo'lmaydi. Ayniqsa performance muammolari, asinxron kod, callback tartibini tushunish uchun Event Loop bilish shart.


JavaScript — single-threaded

JavaScript bitta thread da ishlaydi. Bu nima degani?

Tasavvur qiling — restoranda bitta ofitsiant. U bir vaqtda faqat bitta ish qila oladi. Birinchi stolga buyurtma oladi, oshxonaga olib boradi, kutadi, taom tayyor bo'lganda olib keladi. Bu vaqtda ikkinchi stol kutib turadi.

Bu — blocking (to'suvchi) model. Bir ish tugamaguncha keyingisi boshlanmaydi.

// Blocking kod
const data = fs.readFileSync('fayl.txt'); // kutadi
console.log(data);
console.log('Tugadi'); // fayl o'qilguncha bu qator ishlamaydi

Lekin Node.js da bunday emas. Node.js non-blocking — to'smaydi.

// Non-blocking kod
fs.readFile('fayl.txt', (err, data) => {
  console.log(data);
});
console.log('Tugadi'); // darhol ishlaydi, fayl o'qilishini kutmaydi

Qanday qilib bitta thread ikki ishni parallel qiladi? Javob — Event Loop.


Event Loop — oddiy tushuntirish

Event Loop — bu Node.js ning "boshqaruvchisi". U doimo aylanib turadi va quyidagilarni qiladi:

1. Call Stack bo'shmi? → tekshiradi
2. Bo'sh bo'lsa → navbatdan keyingi callback ni oladi
3. Call Stack ga qo'yadi → bajariladi
4. Qaytadan 1-qadamga

Vizual:

┌───────────────────────────┐
│   Call Stack (JavaScript) │  ← hozir bajarilayotgan kod
└───────────────────────────┘
            ↑
            │ Event Loop
            │ (doimo tekshiradi)
            ↓
┌───────────────────────────┐
│   Callback Queue          │  ← kutayotgan callbacklar
│   [callback1, callback2]  │
└───────────────────────────┘

Amaliy misol — qanday ishlaydi?

console.log('1');

setTimeout(() => {
  console.log('2');
}, 0);

console.log('3');

Natija nima bo'ladi? 1, 2, 3 emas! 1, 3, 2 bo'ladi.

Nima uchun? Keling, qadam-baqadam ko'ramiz:

Qadam 1: console.log('1')
  → Call Stack ga tushadi
  → Bajariladi: "1" chiqadi
  → Call Stack dan chiqadi

Qadam 2: setTimeout(() => { console.log('2') }, 0)
  → Call Stack ga tushadi
  → libuv ga topshiriladi: "0ms dan keyin callback ni navbatga qo'y"
  → Call Stack dan chiqadi (kutmaydi!)

Qadam 3: console.log('3')
  → Call Stack ga tushadi
  → Bajariladi: "3" chiqadi
  → Call Stack dan chiqadi

Qadam 4: Call Stack bo'sh
  → Event Loop tekshiradi: navbatda callback bormi?
  → Bor: setTimeout callback
  → Call Stack ga qo'yadi
  → Bajariladi: "2" chiqadi

Natija:

1
3
2

setTimeout(..., 0) ham darhol ishlamaydi — chunki u callback navbatiga tushadi. Call Stack bo'shagandan keyin ishlaydi.


Brauzer Event Loop vs Node.js Event Loop

Ikkalasida ham Event Loop bor, lekin farqlari bor.

Brauzer Event Loop

Brauzer da Event Loop oddiy:

┌─────────────────────────────────────┐
│  1. Call Stack                      │
│  2. Microtask Queue (Promise)       │
│  3. Macrotask Queue (setTimeout)    │
│  4. Render (DOM yangilash)          │
└─────────────────────────────────────┘

Har bir macrotask dan keyin — barcha microtasklar bajariladi, keyin render.

Node.js Event Loop

Node.js da Event Loop fazalarga bo'lingan. Har bir fazaning o'z navbati bor:

┌───────────────────────────┐
│        timers             │  ← setTimeout, setInterval
├───────────────────────────┤
│   pending callbacks       │  ← I/O callbacklar (TCP xatolari)
├───────────────────────────┤
│      idle, prepare        │  ← ichki ishlar
├───────────────────────────┤
│         poll              │  ← yangi I/O eventlar
├───────────────────────────┤
│         check             │  ← setImmediate
├───────────────────────────┤
│    close callbacks        │  ← socket.on('close', ...)
└───────────────────────────┘

Har bir faza o'z navbatini to'liq bajaradi, keyin keyingisiga o'tadi.

Asosiy farq: Brauzer har bir task dan keyin microtasklar ni bajaradi. Node.js esa har bir faza dan keyin microtasklar ni bajaradi.


Microtask va Macrotask

Bu ikki tushunchani tushunish muhim.

Macrotask — oddiy callback. Navbatda kutadi, o'z navbati kelganda ishlaydi.

setTimeout(() => {}, 0);      // macrotask
setInterval(() => {}, 100);   // macrotask
setImmediate(() => {});       // macrotask (faqat Node.js da)

Microtask — yuqori ustuvorlikka ega callback. Har qanday macrotask dan oldin ishlaydi.

Promise.resolve().then(() => {}); // microtask
process.nextTick(() => {});       // microtask (Node.js da eng yuqori)
queueMicrotask(() => {});         // microtask

Tartib qoidasi

1. Call Stack bo'shaganda
2. Barcha microtasklar bajariladi (to'liq)
3. Bitta macrotask bajariladi
4. Yana barcha microtasklar
5. Yana bitta macrotask
...

Misol

console.log('1');

setTimeout(() => console.log('2'), 0);

Promise.resolve().then(() => console.log('3'));

console.log('4');

Natija:

1
4
3
2

Nima uchun?

1. console.log('1') → "1"
2. setTimeout → macrotask navbatiga
3. Promise.then → microtask navbatiga
4. console.log('4') → "4"
5. Call Stack bo'sh → microtasklar bajariladi → "3"
6. Macrotask bajariladi → "2"

process.nextTick — Node.js ning maxsus microtaski

Node.js da process.nextTick — eng yuqori ustuvorlikka ega microtask. U hatto Promise dan ham oldin ishlaydi.

console.log('1');

setTimeout(() => console.log('2'), 0);

Promise.resolve().then(() => console.log('3'));

process.nextTick(() => console.log('4'));

console.log('5');

Natija:

1
5
4
3
2

Tartib:

1. Sinxron kod: 1, 5
2. process.nextTick: 4
3. Promise microtask: 3
4. setTimeout macrotask: 2

⚠️ Diqqat: process.nextTick ni ko'p ishlatmang. Agar u ichida yana process.nextTick chaqirsangiz — cheksiz loop hosil bo'ladi va Event Loop boshqa hech narsani bajara olmaydi.


Nima uchun Event Loop muhim?

1. Performance

Event Loop tushunmasdan performance muammolarini hal qilib bo'lmaydi. Agar biror kod Event Loop ni "bloklasa" — butun server to'xtaydi.

// ❌ Event Loop ni bloklaydi
app.get('/slow', (req, res) => {
  let sum = 0;
  for (let i = 0; i < 10_000_000_000; i++) {
    sum += i;
  }
  res.send({ sum });
});

Bu vaqtda boshqa hech qanday so'rov bajarilmaydi — chunki Call Stack band.

2. Callback tartibini tushunish

Event Loop bilmasdan callback tartibini bashorat qilib bo'lmaydi.

setTimeout(() => console.log('A'), 0);
setImmediate(() => console.log('B'));

Bu kod ba'zan A, B chiqaradi, ba'zan B, A. Nima uchun? Event Loop fazalari tufayli. Keyingi darsda batafsil ko'ramiz.

3. Memory leak ni topish

Agar callback hech qachon chaqirilmasa — xotira to'lib ketadi. Event Loop tushunish bu muammolarni topishga yordam beradi.


Amalda ko'rish

console.log('Start');

setTimeout(() => {
  console.log('setTimeout 1');
}, 0);

setImmediate(() => {
  console.log('setImmediate 1');
});

Promise.resolve().then(() => {
  console.log('Promise 1');
}).then(() => {
  console.log('Promise 2');
});

process.nextTick(() => {
  console.log('nextTick 1');
});

process.nextTick(() => {
  console.log('nextTick 2');
});

console.log('End');

Natija:

Start
End
nextTick 1
nextTick 2
Promise 1
Promise 2
setTimeout 1
setImmediate 1

Tartib:

1. Sinxron: Start, End
2. nextTick navbati: nextTick 1, nextTick 2
3. Promise microtask: Promise 1, Promise 2
4. setTimeout (timers fazasi)
5. setImmediate (check fazasi)

Xulosa

Event Loop — Node.js ning yuragi. U bitta thread da minglab so'rovni boshqarish imkonini beradi — chunki u kutmaydi, balki callback navbatiga qo'yadi va keyingi ishga o'tadi.

Brauzer vs Node.js: Ikkalasida ham Event Loop bor, lekin Node.js da fazalar bor — har bir faza o'z navbatini to'liq bajaradi.

Microtask vs Macrotask: Microtask (Promise, nextTick) yuqori ustuvorlikka ega — har qanday macrotask dan oldin ishlaydi.

process.nextTick — Node.js ning eng yuqori ustuvorlikli microtaski. Ehtiyot bo'ling — cheksiz loop hosil qilishi mumkin.

Event Loop ni tushunish — Node.js dasturchi uchun shart. Keyingi darsda har bir fazani batafsil ko'ramiz.


Keyingi dars

7-dars: Event Loop fazalari — timers, poll, check, close