Mundarija (40)
- Bu darsda
- 1. Nega bu kerak?
- 2. Node'ning ikki "zudlik" navbati
- 2.1 Qoida
- 2.2 CommonJS faylda: nextTick birinchi
- 2.3 Har callback'dan keyin — faza oxirida emas
- 2.4 Qadamma-qadam
- 3. ESM syurprizi
- 3.1 Xuddi shu kod .mjs da
- 3.2 Nega shunday?
- 3.3 Farq faqat modulning yuqori darajasida
- 3.4 Ichma-ich: navbat ichidan navbatga
- 4. Uchta "keyinroq" — bitta jadvalda
- 5. Ochlik (starvation)
- 5.1 nextTick zanjiri taymerni kutdiradi
- 5.2 Promise zanjiri nextTick'ni ham kutdiradi
- 5.3 setImmediate zanjiri ochlik bermaydi
- 6. Qaysi birini tanlash kerak
- 6.1 Sukut: queueMicrotask
- 6.2 process.nextTick qayerda uchraydi
- 6.3 setImmediate — loop'ga nafas berish
- 7. Brauzer bilan farq
- 8. Intervyu savollari
- 8.1 Dars boshidagi savol
- 8.2 Boshqa savollar
- 9. CLI qadami: valyuta va navbatlar
- 10. Ko'p uchraydigan xatolar
- 10.1 "nextTick doim Promise'dan oldin"
- 10.2 nextTick bilan "loop'ga nafas berish"
- 10.3 Rekursiv nextTick yoki Promise
- 10.4 Node tartibini brauzerga ko'chirish
- 10.5 .js fayl qaysi rejimda?
- 11. Mashqlar
- 1-mashq (oson): bo'sh joylarni to'ldiring
- 2-mashq (o'rta): ikki fayl, ikki javob
- 3-mashq (qiyin): Zalgo'ni tuzating
- 4-mashq: CLI qadami — ESM va CJS'ni o'zingizda solishtiring
- 12. Real ishda
- Xulosa
- Manbalar
process.nextTick va microtask navbati Node'da: nextTick, queueMicrotask va setImmediate tartibi
Qisqacha: Node'da har JavaScript callback'idan keyin ikki navbat bo'shatiladi: avval
process.nextTicknavbati, keyin microtask navbati (Promise,queueMicrotask) — shundan keyingina event loop keyingi callback yoki fazaga o'tadi. Shuning uchun.cjsfayldanextTickPromise'dan oldin ishlaydi. ESM (.mjs) faylning yuqori darajasida esa teskari: modul microtask navbati bo'shatilayotgan paytda bajariladi,nextTickPromise'dan keyin chiqadi.setImmediate— bularning hech biriga kirmaydi, u check fazasida.
Bu darsda
process.nextTicknavbati va microtask navbati qachon va qaysi tartibda bo'shatilishini ayta olasiz.- Bir xil kod
.cjsva.mjsfaylda nega har xil tartib berishini tushuntira olasiz. nextTick,queueMicrotask,setImmediatevasetTimeout(0)ni bitta jadvalda taqqoslaysiz.nextTickva Promise zanjiri loop'ni qanday "och qoldirishini" ko'rsatasiz va undan qochasiz.- Intervyudagi "bu kod nimani qaysi tartibda chiqaradi?" savoliga Node'da ham, brauzerda ham to'g'ri javob berasiz.
Oldin bilishingiz kerak: Event loop fazalari chuqur, Microtask va macrotask chuqur, Callbacklar va callback hell ("Zalgo").
1. Nega bu kerak?
Microtask va macrotask chuqur darsida bitta va'da qoldirgan edik: "process.nextTick .cjs faylda Promise microtask'laridan oldin ishlaydi, .mjs faylning yuqori darajasida — keyin. Sababi — 24-qismda". Bugun shu qarzni yopamiz.
Bu farq amalda muhim. Ko'p maqolalarda "nextTick doim Promise'dan oldin" deyilgan — ular CommonJS davrida yozilgan. Yangi Node loyihalari esa ko'pincha ESM'da, valyuta ham.
Mana intervyuning sevimli savoli. Javobni o'ylab ko'ring:
console.log("1");
setTimeout(() => console.log("2"), 0);
Promise.resolve().then(() => console.log("3"));
process.nextTick(() => console.log("4"));
(async () => {
console.log("5");
await null;
console.log("6");
})();
console.log("7");To'g'ri javob — "qaysi faylda?". .cjs da bitta tartib chiqadi, .mjs da boshqasi. Ikkala javobni dars oxiridagi «Intervyu savollari» bo'limida haqiqiy chiqish bilan ko'ramiz.
«Bahor» oshxonasida: oshpaz (V8) bitta buyurtmani tugatgach, keyingi stansiyaga o'tishdan oldin ikki ilgichga qaraydi — "shu zahoti" (nextTick) va "buyurtma tugashi bilan" (microtask). Ikkalasi bo'shamaguncha u joyidan qo'zg'almaydi.
2. Node'ning ikki "zudlik" navbati
2.1 Qoida
Node'da fazalardan tashqari yana ikki navbat bor:
- nextTick navbati (next tick queue) —
process.nextTick(fn)qo'ygan callback'lar. Bu navbat V8'da emas, Node'ning o'zida. - Microtask navbati (microtask queue) — Promise reaksiyalari (
then,catch,finally,awaitdavomi) vaqueueMicrotask. Bu navbat V8'niki — brauzerdagi bilan bir xil.
Qoida: har bir JavaScript callback'i tugagach (taymer, setImmediate, I/O callback'i, skriptning o'zi), Node:
- nextTick navbatini to'liq bo'shatadi;
- keyin microtask navbatini to'liq bo'shatadi;
- bu vaqtda nextTick navbatiga yangi ish tushgan bo'lsa — 1-qadamga qaytadi;
- ikkalasi ham bo'sh bo'lgachgina event loop davom etadi.
flowchart TB
CB["Callback tugadi"] --> NT["nextTick navbati: to'liq"]
NT --> MT["Microtask navbati: to'liq"]
MT --> Q{"nextTick'da yangi ish bormi?"}
Q -->|ha| NT
Q -->|yo'q| L["Event loop: keyingi callback yoki faza"]Nimaga qarang: nextTick va microtask'lar fazalarning ichida emas, har callback'dan keyin. Shuning uchun ular oldingi darsdagi doiraning birorta fazasiga ham kirmaydi.
2.2 CommonJS faylda: nextTick birinchi
Birinchi tajriba. Bu blok .cjs fayl sifatida ishga tushirilgan. .cjs — Node'ning eski modul tizimi CommonJS fayli; uni CommonJS ichidan darsida o'rganamiz, hozir faqat kengaytmasi muhim. O'zingizda tartib.cjs deb saqlang va node tartib.cjs bilan ishga tushiring:
const yoz = (x) => console.log(x);
setTimeout(() => yoz("setTimeout 0"), 0);
process.nextTick(() => yoz("nextTick"));
Promise.resolve().then(() => yoz("Promise.then"));
queueMicrotask(() => yoz("queueMicrotask"));
yoz("sinxron");Konsolda:
sinxron
nextTick
Promise.then
queueMicrotask
setTimeout 0Qoida bo'yicha: skript (birinchi callback) tugadi → nextTick navbati (nextTick) → microtask navbati (Promise.then, queueMicrotask — qo'yilgan tartibda) → event loop timers fazasiga keldi (setTimeout 0). setImmediate ni ataylab qo'shmadik — oldingi darsdan bilasiz, yuqori darajada uning taymer bilan tartibi kafolatlanmagan.
2.3 Har callback'dan keyin — faza oxirida emas
Ikki setImmediate bitta check fazasida turibdi. Ularning har biri o'z microtask'ini qo'yadi. Microtask'lar faza oxirida bajariladimi yoki har callback'dan keyin?
setImmediate(() => {
console.log("immediate 1");
Promise.resolve().then(() => console.log("promise 1"));
process.nextTick(() => console.log("tick 1"));
});
setImmediate(() => {
console.log("immediate 2");
Promise.resolve().then(() => console.log("promise 2"));
});Konsolda:
immediate 1
tick 1
promise 1
immediate 2
promise 2Har callback'dan keyin. immediate 2 ga navbat kelishidan oldin tick 1 va promise 1 bajarildi — fazada boshqa callback kutib turgan bo'lsa ham. Node 11 dan (2018) beri shunday; eski maqolalarda boshqa javob uchrashi mumkin.
Brauzer ham xuddi shu qoidaga amal qiladi. setImmediate o'rniga taymerlar bilan — bu blok saytda ham, Node'da ham bir xil:
setTimeout(() => {
console.log("taymer 1");
Promise.resolve().then(() => console.log("promise 1"));
}, 0);
setTimeout(() => {
console.log("taymer 2");
Promise.resolve().then(() => console.log("promise 2"));
}, 0);Konsolda:
taymer 1
promise 1
taymer 2
promise 22.4 Qadamma-qadam
Endi uchta navbatni bitta kodda kuzatamiz. Kod I/O callback ichida — u yerda .cjs va .mjs farqi yo'q (nega — keyingi bo'limda):
Tekshirib ko'ring:
setImmediatecallback'ining ichidaprocess.nextTickqo'yildi. U keyingi aylanishda ishlaydimi?
Javob
Yo'q, darhol — o'sha setImmediate callback'i tugashi bilan, keyingi callback'dan ham oldin. nextTick navbati har callback'dan keyin bo'shatiladi, aylanishni kutmaydi. Shuning uchun "next tick" nomi chalg'itadi: u "keyingi aylanish" emas, "joriy ishdan keyin darhol".
3. ESM syurprizi
3.1 Xuddi shu kod .mjs da
Endi aynan o'sha kodni ESM fayl sifatida ishga tushiramiz (tartib.mjs, node tartib.mjs):
const yoz = (x) => console.log(x);
setTimeout(() => yoz("setTimeout 0"), 0);
process.nextTick(() => yoz("nextTick"));
Promise.resolve().then(() => yoz("Promise.then"));
queueMicrotask(() => yoz("queueMicrotask"));
yoz("sinxron");Konsolda:
sinxron
Promise.then
queueMicrotask
nextTick
setTimeout 0nextTick ikkinchidan to'rtinchi o'ringa tushdi. Biz ikkala faylni 30 martadan ishga tushirdik — natija har safar bir xil. Bu tasodif emas, qoida.
3.2 Nega shunday?
Node hujjati (process.nextTick bo'limi, 2026-10-23 da tekshirildi) buni ochiq yozadi. Mazmuni:
- nextTick navbati bo'shatilgan har safar, darhol keyin microtask navbati ham bo'shatiladi — CJS'da shuning uchun
nextTickbirinchi. - ESM modullar esa allaqachon microtask navbatining bir qismi sifatida bajariladi. Node microtask'larni bo'shatish jarayonining ichida turibdi. Shuning uchun ESM'da
queueMicrotaskcallback'lari doimnextTickdan oldin.
Oddiyroq qilib aytganda. ESM modulni Node asinxron yuklaydi: avval import'lar topiladi, keyin modul Promise ichida bajariladi. Modulning yuqori darajasidagi kodingiz — aslida bitta microtask. U tugaganda V8 microtask navbatini bo'shatishni davom ettiradi: siz qo'ygan then va queueMicrotask ham shu navbatda. Node'ning nextTick navbatiga esa navbat microtask'lar tugagandagina keladi.
Oshxonada: modulingiz — oshpaz bajarayotgan "buyurtma tugashi bilan" qog'ozlaridan biri. Siz qo'shgan yangi qog'ozlar shu ilgichning oxiriga tushadi va oshpaz uni bo'shatmaguncha "shu zahoti" ilgichiga qaramaydi.
3.3 Farq faqat modulning yuqori darajasida
ESM faylda ham callback ichiga kirsangiz, CJS qoidasi qaytadi:
setTimeout(() => {
process.nextTick(() => console.log("nextTick"));
Promise.resolve().then(() => console.log("Promise.then"));
console.log("taymer ichida");
}, 0);Konsolda:
taymer ichida
nextTick
Promise.thenTaymer callback'i event loop'dan keladi — microtask ichida emas. U tugagach Node odatdagi qoida bilan avval nextTick, keyin microtask navbatini bo'shatadi.
Top-level await dan keyin-chi (Dinamik import va top-level await)?
await null;
process.nextTick(() => console.log("nextTick"));
Promise.resolve().then(() => console.log("Promise.then"));
console.log("await'dan keyin");Konsolda:
await'dan keyin
Promise.then
nextTickHali ham Promise birinchi. await dan keyingi kod — modulning davomi, u ham microtask sifatida ishlaydi. Xulosa: ESM modulning yuqori darajasida (await'dan oldin ham, keyin ham) microtask'lar nextTick dan oldin. Har qanday taymer, I/O yoki setImmediate callback'i ichida — nextTick birinchi.
3.4 Ichma-ich: navbat ichidan navbatga
Endi qiyinroq holat. nextTick ichida Promise, Promise ichida nextTick qo'yamiz. Avval .cjs:
const log = (x) => console.log(x);
process.nextTick(() => {
log("tick 1");
Promise.resolve().then(() => log("promise tick 1 ichidan"));
});
process.nextTick(() => log("tick 2"));
Promise.resolve().then(() => {
log("promise 1");
process.nextTick(() => log("tick promise 1 ichidan"));
});
Promise.resolve().then(() => log("promise 2"));Konsolda:
tick 1
tick 2
promise 1
promise 2
promise tick 1 ichidan
tick promise 1 ichidanQadamma-qadam: nextTick navbati to'liq (tick 1, tick 2). Keyin microtask navbati to'liq: unda promise 1, promise 2 va tick 1 qo'shgan Promise bor — hammasi bajariladi. promise 1 qo'ygan nextTick esa microtask'lar tugashini kutadi va eng oxirida chiqadi.
Xuddi shu kod .mjs da:
const log = (x) => console.log(x);
process.nextTick(() => {
log("tick 1");
Promise.resolve().then(() => log("promise tick 1 ichidan"));
});
process.nextTick(() => log("tick 2"));
Promise.resolve().then(() => {
log("promise 1");
process.nextTick(() => log("tick promise 1 ichidan"));
});
Promise.resolve().then(() => log("promise 2"));Konsolda:
promise 1
promise 2
tick 1
tick 2
tick promise 1 ichidan
promise tick 1 ichidanJadvalda qadamma-qadam (p — Promise, t — nextTick):
| Qadam | Nima bajariladi | Konsolda |
|---|---|---|
| 1 | Modul kodi tugadi. Navbatlar: nextTick — t1, t2; microtask — p1, p2 | — |
| 2 | Microtask navbati (biz hali modul ichidamiz). p1 nextTick navbatiga yangi tick qo'shadi | promise 1, promise 2 |
| 3 | nextTick navbati to'liq: t1, t2 va p1 qo'shgan tick. t1 microtask navbatiga yangi Promise qo'shadi | tick 1, tick 2, tick promise 1 ichidan |
| 4 | Microtask navbati yana: t1 qo'shgan Promise | promise tick 1 ichidan |
Bu yerda microtask'lar birinchi (modul ichidamiz). Keyin nextTick navbati to'liq — unda uchta ish: tick 1, tick 2 va promise 1 qo'shgan tick. Oxirida tick 1 qo'shgan Promise. Ikki tartib butunlay boshqa — kod esa bir xil.
Tekshirib ko'ring:
.mjsfaylning yuqori darajasidaprocess.nextTickcallback'ini Promise'dan oldin ishlatish kerak. Kodni qanday o'zgartirasiz?
Javob
Ikkalasini biror callback ichiga qo'ying — masalan setTimeout(() => { ... }, 0) yoki setImmediate(() => { ... }). Callback event loop'dan keladi va undan keyin CJS qoidasi ishlaydi. Lekin yaxshiroq yo'l — tartibga bunchalik bog'liq kod yozmaslik: ikkita ish ketma-ket bo'lishi kerak bo'lsa, ikkinchisini birinchisining ichidan chaqiring.
4. Uchta "keyinroq" — bitta jadvalda
Oldingi darsdagi setImmediate va setTimeout(0) ni bugungi navbatlar bilan birlashtiramiz:
| API | Navbat | Qachon ishlaydi | Brauzerda |
|---|---|---|---|
process.nextTick(fn) |
nextTick (Node) | joriy callback'dan keyin darhol | yo'q |
queueMicrotask(fn), then |
microtask (V8) | nextTick'dan keyin (ESM yuqorisida — oldin) | bor |
setImmediate(fn) |
check fazasi | shu yoki keyingi aylanishning check'ida | yo'q |
setTimeout(fn, 0) |
timers fazasi | kamida 1 ms dan keyin, timers'da | bor (0 ms) |
Nomlar teskari tuyuladi: nextTick "keyingi aylanish" degani emas, u eng tezi; setImmediate esa "darhol" emas, u eng sekinlaridan. Node'ning o'z qo'llanmasida ham "nomlarni almashtirish kerak edi, lekin endi kech — ko'p paketlar buzilib ketadi" deyilgan.
5. Ochlik (starvation)
5.1 nextTick zanjiri taymerni kutdiradi
Microtask va macrotask chuqur darsida microtask ochligini ko'rgan edingiz. nextTick bilan ham xuddi shunday — navbat bo'shamaguncha loop qimirlamaydi:
let n = 0;
function yana() {
n++;
console.log("nextTick", n);
if (n < 4) process.nextTick(yana);
}
setTimeout(() => console.log("taymer"), 0);
process.nextTick(yana);Konsolda:
nextTick 1
nextTick 2
nextTick 3
nextTick 4
taymerHar yana() yangi nextTick qo'shadi va navbat hech bo'shamaydi — to'rtta chegara qo'yganimiz uchungina taymerga navbat keldi. Chegara bo'lmasa, taymer hech qachon ishlamaydi. Fayl o'qish, tarmoq javobi, server so'rovlari ham — hammasi kutadi.
Qancha kutishini o'lchadik: zanjir 20 ms davom etadigan qilib yozdik (har tick vaqtni tekshiradi). Natija vaqtga bog'liq, shuning uchun bu blok tekshirilmaydi:
const boshi = Date.now();
let n = 0;
setTimeout(() => {
const ms = Date.now() - boshi;
console.log(`taymer ${ms} ms da, ${n} ta tick'dan keyin`);
}, 0);
function yana() {
n++;
if (Date.now() - boshi < 20) process.nextTick(yana);
}
process.nextTick(yana);Biz uch marta ishga tushirdik (Node 24.21.0, Windows 11, i5-12500H):
taymer 20 ms da, 13039 ta tick'dan keyin
taymer 20 ms da, 11859 ta tick'dan keyin
taymer 20 ms da, 3971 ta tick'dan keyin0 ms li taymer roppa-rosa 20 ms kutdi — zanjir tugashini. Tick'lar soni har safar boshqa, lekin "20 ms" o'zgarmadi.
5.2 Promise zanjiri nextTick'ni ham kutdiradi
Navbatlar bir-birini ham och qoldiradi. ESM modulida microtask'lar birinchi ekanini bilasiz. Promise zanjiri nextTick ni kutdiradi:
let n = 0;
function yana() {
n++;
console.log("Promise", n);
if (n < 3) Promise.resolve().then(yana);
}
process.nextTick(() => console.log("nextTick"));
Promise.resolve().then(yana);Konsolda:
Promise 1
Promise 2
Promise 3
nextTick5.3 setImmediate zanjiri ochlik bermaydi
setImmediate ichida yana setImmediate qo'ysangiz, yangisi keyingi aylanishning check fazasiga tushadi. Orada timers va poll ham o'z navbatini oladi. 20 ms davom etadigan setImmediate zanjiri va 5 ms li taymer:
let n = 0;
const boshi = Date.now();
setTimeout(() => console.log(`taymer: ${n}-aylanishda`), 5);
function yana() {
n++;
if (Date.now() - boshi < 20) setImmediate(yana);
}
setImmediate(yana);Biz uch marta ishga tushirdik:
taymer: 511-aylanishda
taymer: 565-aylanishda
taymer: 589-aylanishdaTaymer o'z vaqtida — zanjir hali davom etayotganda — ishladi. Zanjir har aylanishda loop'ga "nafas" beradi. Uzun ishni bo'laklashda aynan shu xususiyatdan foydalanamiz (Event loop'ni bloklash).
6. Qaysi birini tanlash kerak
6.1 Sukut: queueMicrotask
Node hujjatining tavsiyasi: ko'p hollarda process.nextTick o'rniga queueMicrotask ishlating. Sabablari:
- Brauzerda ham, Node'da ham, Deno va Bun'da ham bir xil ishlaydi.
- Tartibi Promise bilan bir navbatda — CJS/ESM farqi uni chalkashtirmaydi.
6.2 process.nextTick qayerda uchraydi
nextTick eski emas va taqiqlanmagan. U Node'ning o'z kutubxonalarida va ko'p eski paketlarda bor. Ikki odatiy vazifasi:
- Callback'ni doim asinxron qilish. Callbacklar va callback hell darsidagi "Zalgo"ni eslang: funksiya ba'zan callback'ni darhol, ba'zan keyin chaqirsa, uni ishlatgan kod chalkashadi. Javob keshda bo'lsa ham
nextTickbilan keyinroq chaqirish — klassik yechim. - Hodisani konstruktordan keyin chiqarish. Obyekt yaratilayotganda hodisa chiqarilsa, tinglovchi hali ulanmagan bo'ladi.
nextTickhodisani "ulanishdan keyin"ga suradi — buni EventEmitter asoslari darsida ko'ramiz.
nextTick ning bitta qulayligi bor: u callback'ga argumentlarni ham uzatadi — process.nextTick(fn, a, b). queueMicrotask bunday qila olmaydi. Birinchi vazifa misolida:
const kesh = new Map();
function narxniOl(taom, callback) {
if (kesh.has(taom)) {
process.nextTick(callback, null, kesh.get(taom));
return;
}
setTimeout(() => {
kesh.set(taom, 35000);
callback(null, 35000);
}, 10);
}
narxniOl("osh", (xato, narx) => {
console.log("1-chaqiruv:", narx);
narxniOl("osh", (xato2, narx2) => {
console.log("2-chaqiruv:", narx2);
});
console.log("2-so'rov yuborildi");
});Konsolda:
1-chaqiruv: 35000
2-so'rov yuborildi
2-chaqiruv: 35000Ikkinchi chaqiruvda narx keshdan olindi, lekin callback baribir "so'rov yuborildi" dan keyin ishladi. Chaqiruvchi kod uchun kesh bor-yo'qligi farq qilmaydi — doim asinxron. kesh.has tarmog'ida callback(null, kesh.get(taom)) ni to'g'ridan chaqirsangiz, 2-chaqiruv qatori 2-so'rov yuborildi dan oldin chiqadi.
6.3 setImmediate — loop'ga nafas berish
Og'ir ishni bo'laklarga bo'lib, orasida loop'ga I/O va taymerlarni bajarishga imkon berish kerak bo'lsa — setImmediate. nextTick yoki microtask bu ish uchun yaramaydi: ochlik bo'limida ko'rdingiz, ular loop'ni qo'yib yubormaydi.
| Vazifa | Tanlov |
|---|---|
| "Joriy kod tugagach, boshqa hech narsadan oldin" | queueMicrotask |
| Callback'ni doim asinxron chaqirish (callback API) | process.nextTick |
| Uzun ishni bo'laklash, I/O'ga yo'l berish | setImmediate |
| Aniq vaqtdan keyin | setTimeout |
7. Brauzer bilan farq
| Brauzer | Node | |
|---|---|---|
| Microtask navbati | bor | bor (o'sha V8 navbati) |
process.nextTick |
yo'q | bor, microtask'dan alohida |
setImmediate |
yo'q | check fazasi |
.cjs/.mjs farqi |
yo'q | nextTick tartibiga ta'sir qiladi |
Umumiy qism — Promise, async/await, queueMicrotask, setTimeout. Intervyu savolidagi nextTick qatorini olib tashlasak, kod ikkala muhitda bir xil natija beradi. Saytda «Ishga tushir» bilan sinang:
console.log("1");
setTimeout(() => console.log("2"), 0);
Promise.resolve().then(() => console.log("3"));
(async () => {
console.log("5");
await null;
console.log("6");
})();
console.log("7");Konsolda:
1
5
7
3
6
2async funksiya await gacha sinxron ishlaydi (5), await dan keyingi qism microtask (6) — then (3) dan keyin, chunki u keyinroq navbatga tushdi.
8. Intervyu savollari
8.1 Dars boshidagi savol
.cjs faylda (node savol.cjs):
console.log("1");
setTimeout(() => console.log("2"), 0);
Promise.resolve().then(() => console.log("3"));
process.nextTick(() => console.log("4"));
(async () => {
console.log("5");
await null;
console.log("6");
})();
console.log("7");Konsolda:
1
5
7
4
3
6
2.mjs faylda (node savol.mjs):
console.log("1");
setTimeout(() => console.log("2"), 0);
Promise.resolve().then(() => console.log("3"));
process.nextTick(() => console.log("4"));
(async () => {
console.log("5");
await null;
console.log("6");
})();
console.log("7");Konsolda:
1
5
7
3
6
4
2Yaxshi javob: "Sinxron qism — 1 5 7. Keyin fayl turiga bog'liq: CommonJS'da nextTick microtask'lardan oldin, ESM modulida — keyin. Taymer 2 doim oxirida".
8.2 Boshqa savollar
- "
process.nextTickvasetImmediatefarqi?" — nextTick joriy callback'dan keyin darhol, fazaga kirmaydi; setImmediate — check fazasida. - "
nextTickni rekursiv chaqirsa?" — loop och qoladi: taymer va I/O kutadi.
9. CLI qadami: valyuta va navbatlar
Bu darsda valyuta ga kod qo'shilmaydi va commit yo'q — loyiha process obyekti darsida boshlanadi.
Yakuniy kanon kodida (19 ta manba fayl va testlar) birorta ham process.nextTick, queueMicrotask yoki setImmediate yo'q — qidirib tekshirdik. CLI async/await bilan yozilgan: microtask'lar bor, lekin ular await ichida yashirin, qo'lda navbat boshqarish kerak bo'lmadi. Bitta buyruq, bitta foydalanuvchi — ishni bo'laklash yoki "keyinga surish" ehtiyoji yo'q.
Bitta fakt baribir foydali. valyuta ning kirish fayli ESM va unda top-level await bor (process.exitCode = await main(...)). Demak u ishlaganda bugungi "ESM yuqori darajasi" qoidasi amal qiladi. Kodga process.nextTick qo'shsangiz, u maqolalardagidek "eng birinchi" emas, Promise'lardan keyin ishlaydi.
Kanon tajribasi (t03-tartib, Node 24.21.0) ham shuni ko'rsatdi: ESM modul darajasida sinxron → Promise.then → queueMicrotask → nextTick → setTimeout 0, I/O callback ichida esa nextTick → Promise.then → setImmediate → setTimeout 0.
10. Ko'p uchraydigan xatolar
10.1 "nextTick doim Promise'dan oldin"
Faqat CommonJS'da va callback ichida to'g'ri. ESM modulining yuqori darajasida — teskari. Maqoladagi tartib misolini o'qiyotganda, avval fayl turiga qarang: require bormi yoki import?
10.2 nextTick bilan "loop'ga nafas berish"
Uzun hisobni bo'laklab, har bo'lakdan keyin process.nextTick(davomi) qilish — foydasiz: nextTick navbati bo'shamaguncha loop baribir to'xtab turadi. Bo'laklash uchun — setImmediate.
10.3 Rekursiv nextTick yoki Promise
Chegarasiz zanjir dasturni qotiradi: CPU band, lekin so'rovlarga javob yo'q. Xato xabari ham chiqmaydi — shuning uchun topish qiyin. Rekursiyada chegara qo'ying yoki setImmediate ga o'ting.
10.4 Node tartibini brauzerga ko'chirish
Intervyu yechimini brauzer konsolida tekshirmoqchi bo'lsangiz:
Uncaught ReferenceError: process is not definedTarjimasi: "ushlanmagan xato: process aniqlanmagan". Brauzerda process ham, setImmediate ham yo'q. Brauzer uchun misollarni faqat Promise, queueMicrotask va setTimeout bilan yozing.
10.5 .js fayl qaysi rejimda?
.js fayl package.json dagi "type" maydoniga qarab CommonJS yoki ESM bo'ladi. Bir xil tartib.js ikki loyihada ikki xil tartib berishi mumkin. Tartib tajribalari uchun aniq kengaytma ishlating: .cjs yoki .mjs. "type" ni ES Modules Node'da darsida ko'ramiz.
11. Mashqlar
Mashqlar kurs/mashqlar/24/04-microtask-nexttick/ da.
1-mashq (oson): bo'sh joylarni to'ldiring
thenvaqueueMicrotaskcallback'lari tushadigan navbat — ..cjsfaylda yuqori darajada birinchi ishlaydigan navbat — .- Brauzerda yo'q, check fazasida ishlaydigan funksiya — .
- Node hujjati sukut bo'yicha tavsiya qiladigan funksiya — .
Yechim
microtask — V8 navbati, Promise reaksiyalari va queueMicrotask. nextTick — CJS'da callback'dan keyin birinchi bo'shatiladi. setImmediate — check fazasi, faqat Node. queueMicrotask — hamma muhitda bir xil va CJS/ESM farqiga bog'liq emas.
2-mashq (o'rta): ikki fayl, ikki javob
Shu kod .cjs va .mjs faylda nima chiqaradi? Ikkala javobni ham ishga tushirmasdan yozing, keyin tekshiring.
process.nextTick(() => console.log("A"));
Promise.resolve().then(() => {
console.log("B");
process.nextTick(() => console.log("C"));
});
setImmediate(() => console.log("D"));
console.log("E");Yechim
.cjs:
process.nextTick(() => console.log("A"));
Promise.resolve().then(() => {
console.log("B");
process.nextTick(() => console.log("C"));
});
setImmediate(() => console.log("D"));
console.log("E");Konsolda:
E
A
B
C
D.mjs:
process.nextTick(() => console.log("A"));
Promise.resolve().then(() => {
console.log("B");
process.nextTick(() => console.log("C"));
});
setImmediate(() => console.log("D"));
console.log("E");Konsolda:
E
B
A
C
DE — sinxron, ikkalasida birinchi. CJS'da: nextTick navbati (A), microtask (B, u C ni qo'shadi), yana nextTick (C), keyin check (D). ESM'da: microtask birinchi (B — C ni nextTick navbatiga A ning orqasiga qo'yadi), keyin nextTick navbati to'liq (A, C), keyin check (D). Bu yerda setImmediate yuqori darajada bo'lsa ham xavfsiz: yonida taymer yo'q, u doim eng oxirida.
3-mashq (qiyin): Zalgo'ni tuzating
matnniOl(callback) faylni bir marta o'qiydi va keshlaydi. sinov() chaqiruvdan keyin tayyor = true qiladi va callback "tayyor" holatini chiqaradi. Kodni ishga tushiring: ikkinchi chaqiruv nega false chiqaradi? Keyin matnniOl ni shunday tuzatingki, ikkala chaqiruv ham true chiqarsin. Ishora: process.nextTick(fn, arg) argumentni ham uzatadi — ushbu darsdagi «Qaysi birini tanlash kerak» bo'limiga qarang.
import { readFile } from "node:fs";
let kesh = null;
function matnniOl(callback) {
if (kesh !== null) {
callback(kesh);
return;
}
readFile(import.meta.filename, "utf8", (xato, matn) => {
kesh = matn;
callback(matn);
});
}
function sinov(nomi) {
let tayyor = false;
matnniOl(() => console.log(nomi, "tayyor:", tayyor));
tayyor = true;
}
sinov("1-chaqiruv");
setTimeout(() => sinov("2-chaqiruv"), 50);Konsolda:
1-chaqiruv tayyor: true
2-chaqiruv tayyor: falseYechim
Birinchi chaqiruvda fayl o'qiladi — callback keyin ishlaydi, tayyor allaqachon true. Ikkinchisida matn keshda — callback darhol, tayyor = true qatoridan oldin chaqiriladi. Bitta funksiya ikki xil xulq ko'rsatdi — bu Zalgo. Tuzatish — kesh tarmog'ida ham callback'ni keyinga surish:
import { readFile } from "node:fs";
let kesh = null;
function matnniOl(callback) {
if (kesh !== null) {
process.nextTick(callback, kesh);
return;
}
readFile(import.meta.filename, "utf8", (xato, matn) => {
kesh = matn;
callback(matn);
});
}
function sinov(nomi) {
let tayyor = false;
matnniOl(() => console.log(nomi, "tayyor:", tayyor));
tayyor = true;
}
sinov("1-chaqiruv");
setTimeout(() => sinov("2-chaqiruv"), 50);Konsolda:
1-chaqiruv tayyor: true
2-chaqiruv tayyor: truequeueMicrotask(() => callback(kesh)) ham ishlaydi — u ham tayyor = true dan keyin bajariladi. Ikkinchi chaqiruv taymer callback'i ichida bo'lgani uchun ikkalasi ham shu callback tugagach darhol ishlaydi. Yangi kodda esa Promise qaytaruvchi funksiya (fs/promises) yaxshiroq: await doim asinxron.
4-mashq: CLI qadami — ESM va CJS'ni o'zingizda solishtiring
valyuta ga bu darsda kod qo'shilmaydi. Kanon tajribasini o'z kompyuteringizda takrorlang:
- «CommonJS faylda: nextTick birinchi» bo'limidagi kodni
tartib.cjsvatartib.mjsga saqlang. Ikkalasini 5 martadan ishga tushiring. Tartib o'zgaradimi? tartib.mjsdagi butun kodnisetTimeout(() => { ... }, 0)ichiga o'rang.nextTickqayerga ko'chdi?node --versionni yozib qo'ying va natijangizni darsdagi bilan solishtiring.
Yechim
- Har fayl o'z ichida barqaror:
.cjs—sinxron, nextTick, Promise.then, queueMicrotask, setTimeout 0;.mjs—sinxron, Promise.then, queueMicrotask, nextTick, setTimeout 0. Biz har birini 30 martadan ishga tushirdik — bir xil. - O'ralgan
.mjsCJS tartibini beradi —nextTickyanasinxrondan keyin birinchi. Kod endi modul darajasida emas, taymer callback'ida; callback tugagach Node odatdagidek avval nextTick navbatini bo'shatadi. IchkisetTimeout 0esa keyingi aylanishda. - Darslar Node 24.21.0 da tekshirilgan. Bu tartib Node hujjatida yozilgan xulq; boshqa versiyada farq chiqsa, o'sha versiya hujjatining
process.nextTickbo'limini o'qing.
12. Real ishda
- Intervyu: "Bu kod qaysi tartibda chiqaradi?" — Node intervyusida deyarli doim
nextTickbilan keladi. - Kutubxona kodi: Node'ning
streamvaeventsmodullari, eski callback API'li paketlar ichidaprocess.nextTickko'p. - Osilib qolgan server: CPU 100 %, lekin so'rovlarga javob yo'q, xato logi bo'sh — sabablaridan biri cheksiz
nextTickyoki Promise zanjiri. - ESM'ga ko'chish: CommonJS loyihani ESM'ga o'tkazganda, yuqori darajadagi
nextTickva Promise tartibiga tayangan kod (masalan modul yuklanishidagi ishga tushirish) o'zgacha ishlay boshlaydi. Ko'chish testlarida buni tekshiring.
Xulosa
- Har JavaScript callback'idan keyin Node avval nextTick navbatini, keyin microtask navbatini to'liq bo'shatadi — shundan keyin event loop davom etadi.
- CommonJS faylda va har qanday callback ichida
nextTickPromise'dan oldin; ESM modulining yuqori darajasida — keyin, chunki modul o'zi microtask ichida bajariladi. setImmediate— check fazasi,setTimeout(0)— timers fazasi; ikkalasi ham microtask va nextTick'dan keyin.- nextTick va Promise zanjiri loop'ni och qoldiradi (biz 0 ms li taymerni 20 ms kutdirdik);
setImmediatezanjiri — yo'q. - Sukut tanlov —
queueMicrotask;nextTick— callback'ni doim asinxron qilish uchun;setImmediate— ishni bo'laklash uchun.
Keyingi dars: libuv thread pool va OS asinxron I/O — fayl, crypto va dns qaysi thread'larda bajariladi va 4 ta ishchi thread qachon tor joyga aylanadi.
Manbalar
- Node.js API —
process.nextTickva "When to usequeueMicrotask()vs.process.nextTick()": https://nodejs.org/api/process.html#processnexttickcallback-args - Node.js qo'llanmasi — The Node.js Event Loop (
process.nextTickvasetImmediate): https://nodejs.org/en/learn/asynchronous-work/event-loop-timers-and-nexttick - MDN — Using microtasks in JavaScript with
queueMicrotask(): https://developer.mozilla.org/en-US/docs/Web/API/HTML_DOM_API/Microtask_guide
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!