7-bo‘lim
Hodisalar sikli
Sinxron va asinxron kod tartibi, mikro va makro vazifalar, setTimeout va setImmediate, nextTick ning ESM dagi kutilmagan o'rni va siklni bloklash.
Ushbu bo‘lim mundarijasi
Node bitta oqimda ishlaydi, lekin bir vaqtda minglab so'rovga javob bera oladi. Buni hodisalar sikli (event loop) ta'minlaydi.
Bu bo'lim Node ning eng chalkash qismi haqida - lekin uni tushunish keyingi hamma narsani osonlashtiradi.
Sinxron avval #
console.log("1");
setTimeout(() => console.log("3 - setTimeout"), 0);
Promise.resolve().then(() => console.log("2 - promise"));
console.log("1.5");
1
1.5
2 - promise
3 - setTimeout
Qoida oddiy: hamma sinxron kod birinchi tugaydi. Faqat shundan keyin navbatdagi vazifalar bajariladi.
setTimeout(..., 0) "darhol" degani emas - u "sinxron kod
tugagach, birinchi imkoniyatda" degani.
Ikki xil navbat #
console.log("boshlandi");
setTimeout(() => console.log("makro: setTimeout"), 0);
Promise.resolve()
.then(() => console.log("mikro: birinchi"))
.then(() => console.log("mikro: ikkinchi"));
queueMicrotask(() => console.log("mikro: queueMicrotask"));
console.log("tugadi");
boshlandi
tugadi
mikro: birinchi
mikro: queueMicrotask
mikro: ikkinchi
makro: setTimeout
Diqqat qiling: mikro: ikkinchi queueMicrotask dan keyin
chiqdi.
Sababi - zanjir: birinchi .then bajarilgach, ikkinchisi navbatga
yangi qo'shiladi, queueMicrotask esa allaqachon navbatda
turgan edi.
nextTick va ESM #
Endi Node ning eng chalkash burchagi.
const izlar = [];
Promise.resolve().then(() => izlar.push("promise"));
queueMicrotask(() => izlar.push("queueMicrotask"));
process.nextTick(() => izlar.push("nextTick"));
setTimeout(() => console.log(izlar.join(" -> ")), 10);
promise -> queueMicrotask -> nextTick
Deyarli hamma manbada shunday deyiladi: process.nextTick hamma
mikro vazifalardan oldin bajariladi.
Bu CommonJS uchun to'g'ri. .cjs faylida yuqoridagi kod
nextTick -> promise -> queueMicrotask beradi.
ESM da esa natija teskari. Sabab: ESM moduli o'zi mikro vazifa
ichida bajariladi. Modul tanasi ishlayotganda joriy mikro navbat
hali bo'shatilmagan - shuning uchun u avval oxirigacha bo'shatiladi,
nextTick navbatiga esa faqat keyin o'tiladi.
Bu faqat modulning eng yuqori darajasida shunday. Xuddi shu kod
funksiya ichida - masalan setTimeout yoki so'rov ishlovchisida -
odatiy tartibni beradi: nextTick birinchi.
Amaliy xulosa: process.nextTick ga tayangan kod yozmang.
Uning Promise dan farqi juda nozik va kontekstga bog'liq. Deyarli
har doim queueMicrotask yoki oddiy Promise yetarli.
Buni isbotlaymiz - xuddi shu kodni funksiya ichiga qo'yamiz:
const izlar = [];
setTimeout(() => {
Promise.resolve().then(() => izlar.push("promise"));
queueMicrotask(() => izlar.push("queueMicrotask"));
process.nextTick(() => izlar.push("nextTick"));
}, 0);
setTimeout(() => console.log(izlar.join(" -> ")), 20);
nextTick -> promise -> queueMicrotask
Bir xil uch qator, boshqa tartib - farq faqat qayerda yozilganida.
setTimeout va setImmediate #
import { readFile } from "node:fs/promises";
const izlar = [];
await readFile(new URL(import.meta.url));
setTimeout(() => izlar.push("setTimeout"), 0);
setImmediate(() => izlar.push("setImmediate"));
setTimeout(() => console.log(izlar.join(" -> ")), 20);
setImmediate -> setTimeout
setImmediate - Node ga xos funksiya (brauzerda yo'q). U
"joriy aylanish tugagach darhol" degani.
| Funksiya | Qachon ishlaydi |
|---|---|
setTimeout(f, 0) | Taymerlar bosqichida, keyingi aylanishda |
setImmediate(f) | Joriy aylanishning check bosqichida |
process.nextTick(f) | Joriy amaldan keyin darhol |
queueMicrotask(f) | Mikro navbatda |
Kirish-chiqish ishlovchisi ichida setImmediate har doim
setTimeout(f, 0) dan oldin ishlaydi - bu kafolatlangan.
Lekin ikkalasini dasturning eng boshida, hech qanday kirish-chiqishsiz yozsangiz, tartib kafolatlanmaydi: u jarayonning ishga tushish tezligiga bog'liq bo'lib qoladi. Bir xil kod bir kompyuterda bir xil, boshqasida boshqacha chiqishi mumkin.
Shuning uchun yuqoridagi misolda avval fayl o'qildi - shunda natija barqaror bo'ladi.
Umumiy maslahat: bu ikkisining tartibiga tayanmang. Tartib muhim
bo'lsa, uni await bilan ochiq belgilang.
await tartibni o'zgartiradi #
async function ishla(nom) {
console.log(`${nom}: boshlandi`);
await null;
console.log(`${nom}: davom etdi`);
}
console.log("oldin");
ishla("A");
ishla("B");
console.log("keyin");
oldin
A: boshlandi
B: boshlandi
keyin
A: davom etdi
B: davom etdi
async funksiya darhol ishlay boshlaydi - birinchi await
gacha sinxron davom etadi.
await ga yetganda funksiya to'xtaydi va boshqaruv chaqiruvchiga
qaytadi. Qolgan qismi mikro vazifa sifatida navbatga qo'yiladi.
Aynan shuning uchun "A: boshlandi" va "B: boshlandi" oldin, davomlari esa keyin chiqdi.
Siklni bloklash #
function ogirHisob(n) {
let jami = 0;
for (let i = 0; i < n; i++) jami += i;
return jami;
}
const izlar = [];
setTimeout(() => izlar.push("taymer"), 0);
const natija = ogirHisob(5_000_000);
izlar.push("hisob tugadi");
setTimeout(() => {
console.log(izlar.join(" -> "));
console.log("Natija musbat:", natija > 0);
}, 10);
hisob tugadi -> taymer
Natija musbat: true
setTimeout(..., 0) birinchi yozilgan edi, lekin u hisobdan
keyin ishladi.
Sabab: hodisalar sikli sinxron kod tugashini kutadi. Besh million qadamlik sikl ishlayotganda Node hech narsa qila olmaydi - na taymer, na yangi so'rov, na fayl javobi.
Haqiqiy serverda bu shuni bildiradi: bitta og'ir funksiya hamma foydalanuvchini kutishga majbur qiladi.
Nima qilish kerak:
- Og'ir ishni bo'lib tashlang - har bo'lakdan keyin siklga nafas bering.
worker_threads- alohida oqimda hisoblang.- Boshqa jarayonga bering - navbat yoki alohida xizmat.
Eng muhimi - buni bilish. Node "sekin" emas; u shunchaki kutish uchun optimallashtirilgan, hisob uchun emas.
Navbatni bo'shatish #
async function bolibIshla(jami, bolak) {
let natija = 0;
let uzilishlar = 0;
for (let i = 0; i < jami; i++) {
natija += i;
if (i > 0 && i % bolak === 0) {
await new Promise((yechim) => setImmediate(yechim));
uzilishlar++;
}
}
return { natija, uzilishlar };
}
const { natija, uzilishlar } = await bolibIshla(1000, 250);
console.log("Natija:", natija);
console.log("Uzilishlar:", uzilishlar);
Natija: 499500
Uzilishlar: 3
await new Promise((yechim) => setImmediate(yechim)) - siklga
"nafas olish" imkonini beradi.
Har uzilishda Node kutayotgan boshqa ishlarni bajarib oladi va keyin davom etadi.
Taymerlar aniq emas #
const kechikish = 50;
const boshi = process.hrtime.bigint();
setTimeout(() => {
const otgan = Number(process.hrtime.bigint() - boshi) / 1e6;
console.log("Kamida so'ralgancha kutdi:", otgan >= kechikish);
console.log("Aniq songa teng emas:", otgan !== kechikish);
}, kechikish);
Kamida so'ralgancha kutdi: true
Aniq songa teng emas: true
setTimeout(f, 50) - "aynan 50 millisekunddan keyin" degani emas,
balki "kamida 50 millisekunddan keyin" degani.
Agar o'sha paytda sikl band bo'lsa, taymer kechikadi. Shuning uchun Node da aniq vaqtga tayanib bo'lmaydi.
- Sinxron va
setTimeouttartibini kuzating. Promise.thenzanjirining tartibini chizib chiqing.nextTickni ESM ning yuqori darajasida sinang.- Xuddi shuni
setTimeoutichida qayta sinang. - Ikkalasini
.cjsfaylida ham tekshiring. - Kirish-chiqishdan keyin
setImmediatevasetTimeoutni solishtiring. - Ikki
asyncfunksiyani ketma-ket chaqirib, tartibni ko'ring. - Og'ir sikl bilan taymerni kechiktiring.
setImmediatebilan siklga nafas bering.- Taymer aniq vaqtdan kechikishini o'lchang.
Xulosa #
- Sinxron kod har doim birinchi tugaydi.
- Mikro vazifalar (
Promise,queueMicrotask) makro vazifalardan (setTimeout) oldin, va navbat butunlay bo'shatiladi. process.nextTickESM ning yuqori darajasida mikro vazifalardan keyin ishlaydi - CommonJS da esa oldin. Unga tayanmang.setImmediatekirish-chiqish ichidasetTimeout(f, 0)dan oldin; asosiy modulda esa tartib kafolatlanmaydi.asyncfunksiya birinchiawaitgacha sinxron ishlaydi.- Og'ir hisob butun serverni bloklaydi - bu Node ning asosiy xavfi.
- Uzoq siklni
await setImmediatebilan bo'lib, siklga nafas bering. setTimeoutkamida so'ralgancha kutadi, aniq emas.
Keyingi bo'limda hodisalar va EventEmitter ni o'rganamiz.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
Xatolik topdingizmi?
Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.