setImmediate vs setTimeout vs process.nextTick — farqlari
Muammodan boshlaylik
Siz asinxron kod yozyapsiz. Biror ishni "keyinroq" bajarish kerak — lekin qanchalik keyinroq? Darhol keyin? Keyingi iteratsiyada? Belgilangan vaqtdan keyin?
Node.js da buning uchun uchta funksiya bor: process.nextTick, setImmediate, setTimeout. Ko'rinishda o'xshash — lekin ishlash tartibi butunlay boshqacha. Noto'g'ri tanlasangiz — kutilmagan natijalar chiqadi.
Uchala funksiya — qisqacha
process.nextTick(() => {
// Hozirgi operatsiya tugagandan keyin,
// Event Loop ning keyingi fazasiga O'TMASDAN OLDIN ishlaydi
});
Promise.resolve().then(() => {
// process.nextTick dan keyin,
// lekin hali ham keyingi fazaga o'tmasdan ishlaydi
});
setImmediate(() => {
// Event Loop ning check fazasida ishlaydi
// (poll fazasidan keyin)
});
setTimeout(() => {
// Event Loop ning timers fazasida ishlaydi
// (belgilangan ms o'tgandan keyin)
}, 0);
process.nextTick — "hoziroq, lekin keyin"
process.nextTick — Event Loop fazalari orasida emas, faza ichida ishlaydi. Hozirgi sinxron kod tugagandan keyin, keyingi fazaga o'tmasdan oldin — shu yerda ishlaydi.
Sinxron kod ishlaydi
↓
process.nextTick navbati to'liq bajariladi
↓
Promise microtask navbati to'liq bajariladi
↓
Event Loop keyingi fazaga o'tadi
Misol
console.log('1 — sinxron');
process.nextTick(() => {
console.log('2 — nextTick');
});
console.log('3 — sinxron');
Natija:
1 — sinxron
3 — sinxron
2 — nextTick
nextTick callback sinxron kod tugagandan keyin, lekin boshqa hech narsadan oldin ishladi.
Nima uchun kerak?
process.nextTick ning asosiy maqsadi — foydalanuvchiga event handler qo'shish imkonini berish. Masalan, siz class yozyapsiz. Constructor ichida event chiqarsangiz — foydalanuvchi hali handler qo'sha olmagan:
const EventEmitter = require('events');
class Fayl extends EventEmitter {
constructor(nom) {
super();
// ❌ Xato — foydalanuvchi hali 'ready' ga subscribe qilmagan
this.emit('ready', nom);
}
}
const f = new Fayl('test.txt');
f.on('ready', (nom) => console.log(nom)); // hech qachon ishlamaydi!
process.nextTick bilan to'g'ri yozish:
class Fayl extends EventEmitter {
constructor(nom) {
super();
// ✅ To'g'ri — sinxron kod tugagandan keyin emit qiladi
// Foydalanuvchi shu orada handler qo'sha oladi
process.nextTick(() => this.emit('ready', nom));
}
}
const f = new Fayl('test.txt');
f.on('ready', (nom) => console.log(nom)); // ishlaydi!
Xavfli tomoni
process.nextTick ichida yana process.nextTick chaqirsangiz — Event Loop hech qachon keyingi fazaga o'ta olmaydi. I/O to'xtaydi, server javob bermay qoladi.
// ❌ Xavfli — cheksiz loop
function recursive() {
process.nextTick(recursive);
}
recursive();
// Bu kod I/O ni butunlay bloklaydi!
setImmediate — "poll dan keyin"
setImmediate — Event Loop ning check fazasida ishlaydi. Poll fazasi tugagandan keyin, shu aylanishda bajariladi.
poll fazasi tugadi
↓
microtasklar (nextTick, Promise)
↓
check fazasi → setImmediate ishlaydi
↓
close callbacks
↓
keyingi aylanish...
Misol
setImmediate(() => {
console.log('setImmediate');
});
console.log('sinxron');
Natija:
sinxron
setImmediate
Nima uchun kerak?
setImmediate ning asosiy maqsadi — I/O callback ichida "shu aylanishda, lekin I/O dan keyin" biror ish bajarish.
const fs = require('fs');
fs.readFile('fayl.txt', () => {
// Fayl o'qildi. Endi qo'shimcha ish qilish kerak.
// Bu ish shu aylanishda, lekin boshqa I/O lardan keyin bo'lsin.
setImmediate(() => {
console.log('Qo\'shimcha ish bajarildi');
});
});
Katta hajmli ma'lumot qayta ishlashda ham foydali — har iteratsiyada setImmediate bilan Event Loop ga "nafas olish" imkonini berish:
function kattaMassivniQayta(massiv, index = 0) {
if (index >= massiv.length) {
console.log('Tugadi');
return;
}
// Bitta elementni qayta ishla
qaytaIshla(massiv[index]);
// Keyingi elementni keyingi iteratsiyada bajar
// Bu Event Loop ni bloklamaydi
setImmediate(() => kattaMassivniQayta(massiv, index + 1));
}
setTimeout — "N ms dan keyin"
setTimeout — Event Loop ning timers fazasida ishlaydi. Belgilangan millisekund o'tgandan keyin, timers fazasiga kelganda bajariladi.
setTimeout(() => {
console.log('1 soniyadan keyin');
}, 1000);
setTimeout(fn, 0) — nima bu?
0 ms deb yozsangiz ham — bu "darhol" degani emas. Node.js 0 ni kamida 1ms deb qabul qiladi. Va u timers fazasiga kelganda ishlaydi — bu bir necha ms kechikishi mumkin.
setTimeout(() => console.log('setTimeout 0'), 0);
setImmediate(() => console.log('setImmediate'));
Bu kod noaniq natija beradi — ba'zan setTimeout oldin, ba'zan setImmediate oldin. Nima uchun?
Dastur boshlanayotganda:
- Agar timers fazasiga kelganda 1ms o'tgan bo'lsa
→ setTimeout oldin ishlaydi
- Agar 1ms o'tmagan bo'lsa
→ setImmediate oldin ishlaydi (check fazasi)
Bu tizim yukiga, protsessor tezligiga bog'liq.
Kafolatlanmagan.
Uchala funksiyani solishtirish
console.log('--- Boshlanish ---');
setTimeout(() => console.log('setTimeout'), 0);
setImmediate(() => console.log('setImmediate'));
process.nextTick(() => console.log('nextTick'));
Promise.resolve().then(() => console.log('Promise'));
console.log('--- Tugash ---');
Natija:
--- Boshlanish ---
--- Tugash ---
nextTick
Promise
setTimeout ← yoki setImmediate (noaniq)
setImmediate ← yoki setTimeout (noaniq)
Sinxron kod tugagandan keyin:
nextTick— eng birinchi (microtask, yuqori ustuvorlik)Promise— ikkinchi (microtask)setTimeoutvasetImmediate— tartib noaniq (tashqi kontekstda)
I/O ichida — tartib aniq
const fs = require('fs');
fs.readFile(__filename, () => {
console.log('--- I/O callback ---');
setTimeout(() => console.log('setTimeout'), 0);
setImmediate(() => console.log('setImmediate'));
process.nextTick(() => console.log('nextTick'));
Promise.resolve().then(() => console.log('Promise'));
});
Natija — har doim bir xil:
--- I/O callback ---
nextTick
Promise
setImmediate
setTimeout
I/O callback poll fazasida ishlaydi. Poll tugagandan keyin:
nextTick(microtask)Promise(microtask)setImmediate(check fazasi — poll dan keyin)setTimeout(keyingi aylanish — timers fazasi)
Qachon qaysinisini ishlatish kerak?
process.nextTick:
✓ Hozirgi operatsiya tugagandan keyin, lekin
Event Loop fazasi o'zgarmasdan oldin bajarish kerak
✓ Event emitter da emit ni kechiktirish
✓ Xatoni asinxron qaytarish (API consistency)
✗ Rekursiv ishlatmang — I/O ni bloklaydi
setImmediate:
✓ I/O callback ichida "shu aylanishda, lekin keyin"
✓ Katta hisoblashni bo'laklarga bo'lish
✓ setTimeout(fn, 0) o'rniga I/O ichida
✓ Natija kafolatlangan (I/O ichida)
setTimeout(fn, 0):
✓ Brauzer bilan mos kod (universal)
✓ Minimal kechikish kerak bo'lganda
✗ setImmediate bilan solishtirmang — noaniq
Amaliy qoida
I/O callback ichida "keyinroq" bajarish kerak?
→ setImmediate ishlating
Hozirgi sinxron blok tugagandan keyin darhol?
→ process.nextTick ishlating
Belgilangan vaqtdan keyin?
→ setTimeout ishlating
Xulosa
process.nextTick |
setImmediate |
setTimeout(fn, 0) |
|
|---|---|---|---|
| Qayerda ishlaydi | Faza orasida | check fazasi | timers fazasi |
| Ustuvorlik | Eng yuqori | O'rta | O'rta |
| I/O ichida tartib | 1-chi | 2-chi | 3-chi |
| Tashqi kontekstda | 1-chi | Noaniq | Noaniq |
| Xavf | Rekursiyada bloklaydi | Xavfsiz | Xavfsiz |
Eng muhim qoida: setImmediate va setTimeout(fn, 0) ni tashqi kontekstda solishtirmang — natija kafolatlanmagan. I/O ichida esa setImmediate har doim setTimeout dan oldin ishlaydi.
Keyingi dars
9-dars: Blocking vs Non-blocking — nima uchun xavfli