Mundarija (39)
- Bu darsda
- 1. Nega bu kerak?
- 2. Bitta aylanish: oltita faza
- 2.1 Doira
- 2.2 Har faza bir gapdan
- 2.3 Aylanish (tick)
- 3. Timers fazasi
- 3.1 Vaqti kelgan taymerlar
- 3.2 setTimeout(fn, 0) aslida 1 ms
- 4. Poll fazasi — loop'ning kutish joyi
- 4.1 Poll nima qiladi
- 4.2 Poll qancha kutadi?
- 4.3 Loop qachon tugaydi
- 5. Check fazasi va setImmediate
- 5.1 setImmediate — "poll'dan keyin, darhol"
- 5.2 I/O callback ichida: check doim birinchi
- 5.3 Taymer ichida: yana check birinchi
- 5.4 Faylning yuqori darajasida: kafolat yo'q
- 6. Qolgan fazalar: pending, idle/prepare, close
- 6.1 Pending callbacks
- 6.2 Idle va prepare
- 6.3 Close callbacks
- 6.4 libuv hujjati va Node qo'llanmasi
- 7. Brauzer bilan taqqoslash
- 8. CLI qadami: valyuta da fazalar
- 9. Ko'p uchraydigan xatolar
- 9.1 "setImmediate — darhol"
- 9.2 Yuqori darajadagi tartibga tayanish
- 9.3 "Skript tugadi, lekin terminal qaytmayapti"
- 9.4 "Taymer aniq vaqtida ishlaydi"
- 9.5 Brauzerda setImmediate yoki process
- 10. Mashqlar
- 1-mashq (oson): fazalarni joyiga qo'ying
- 2-mashq (o'rta): tartibni oldindan ayting
- 3-mashq (qiyin): keyingiAylanish() yordamchisi
- 4-mashq: CLI qadami — tartibni o'z kompyuteringizda o'lchang
- 11. Real ishda
- Xulosa
- Manbalar
Node.js event loop fazalari: timers, poll, check va bitta aylanish
Qisqacha: Node'ning event loop'i bitta task navbati emas — u fazalar doirasi: timers (
setTimeout), pending, idle/prepare, poll (fayl va tarmoq callback'lari, kutish), check (setImmediate), close. Doiraning bitta to'liq aylanishi — loop aylanishi (tick). Fazalar tartibi sabab I/O callback ichidasetImmediatedoimsetTimeout(0)dan oldin ishlaydi, faylning yuqori darajasida esa ularning tartibi kafolatlanmagan. Navbatda hech narsa qolmasa, loop to'xtaydi va jarayon tugaydi.
Bu darsda
- Node event loop'ining oltita fazasini tartibi bilan ayta olasiz va har biriga qaysi callback tushishini bilasiz.
- Poll fazasi qachon kutishini, qachon kutmasligini tushuntira olasiz.
setImmediatevasetTimeout(0)tartibini uch joyda — I/O callback'ida, taymer ichida va faylning yuqori darajasida — oldindan ayta olasiz.- Node jarayoni nega ba'zan o'zi tugaydi, ba'zan esa "osilib" turishini tushuntira olasiz.
- Fazalarni o'lchov bilan tekshirishni bilasiz: bir marta ishga tushirish yetmaydi.
Oldin bilishingiz kerak: Event loop (task, microtask, call stack), Microtask va macrotask chuqur, Node.js nima, Blocking va non-blocking I/O.
1. Nega bu kerak?
Event loop darsida brauzer qoidasini o'rgangan edingiz: sinxron kod → hamma microtask'lar → bitta task → yana microtask'lar. O'sha darsning oxirida ikki g'alati holat qolgan edi:
- Fayl o'qilgandan keyin
setImmediatedoimsetTimeout(fn, 0)dan oldin ishladi. - Faylning yuqori darajasida esa bu ikkisining tartibi "kompyuterga qarab" o'zgaradi.
Biz buni o'lchab ko'rdik. Ikki qatorli faylni (setTimeout va setImmediate) 100 marta ishga tushirdik: 92 marta setImmediate birinchi chiqdi, 8 marta — setTimeout. Bir xil kod, bir xil kompyuter — natija har xil. Nega?
Javob shu: Node'da "task navbati" bitta emas. Callback'lar turiga qarab fazalarga bo'lingan va event loop ularni doira bo'ylab aylanib o'tadi. Fazalarni bilsangiz, tartibni oldindan aytasiz.
Node.js nima darsida «Bahor» oshxonasini eslang: V8 — oshpaz, libuv — yordamchilar va texnika. Bugun oshpazning aylanma yo'lini ko'ramiz. U oshxonani doim bir xil tartibda aylanib chiqadi: avval qum soatlarga qaraydi, keyin eshikka, keyin "darhol" qog'ozlariga. Qaysi ish qachon bajarilishi shu yo'lga bog'liq.
2. Bitta aylanish: oltita faza
2.1 Doira
Avval rasmga qarang. Skript (faylingizning yuqori darajasi) bir marta bajariladi. Keyin libuv doirani aylana boshlaydi:
flowchart TB
S["Skript bajariladi"] --> T["timers: setTimeout"]
T --> P["pending: kechikkan I/O"]
P --> I["idle, prepare: ichki"]
I --> PO["poll: I/O kutish"]
PO --> C["check: setImmediate"]
C --> CL["close: yopilish"]
CL --> Q{"Loop tirikmi?"}
Q -->|ha| T
Q -->|yo'q| E["Jarayon tugaydi"]Nimaga qarang: o'q doim pastga va yana tepaga qaytadi. Poll'dan keyin har doim check keladi, check'dan keyin esa taymerlarga navbat faqat keyingi aylanishda keladi. Bugungi hamma tartib qoidalari shu bitta rasmdan chiqadi.
2.2 Har faza bir gapdan
Faza (phase) — event loop aylanishining bir bosqichi; har fazaning o'z callback navbati bor. Oshpaz har stansiyada o'sha stansiyaning ishlarini bajaradi, keyin keyingisiga o'tadi.
| Faza | Qaysi callback'lar | «Bahor»da |
|---|---|---|
| timers | vaqti kelgan setTimeout, setInterval |
qum soati tugaganmi? |
| pending callbacks | oldingi aylanishdan qolgan ba'zi tizim I/O callback'lari | kechagi qolgan ish |
| idle, prepare | faqat Node'ning ichki ishi | oshpaz qo'lini yuvadi |
| poll | fayl, tarmoq callback'lari; yangi I/O'ni kutish | eshik: yetkazib beruvchilar |
| check | setImmediate |
"darhol keyin" qog'ozlari |
| close callbacks | yopilgan resurslarning "close" hodisalari |
qozonlarni yig'ishtirish |
Bu fazalarning hammasi libuv ichida. JavaScript kodingiz faqat callback'larda ishlaydi — oshpaz ishni oladi, bajaradi va qaytib aylanma yo'liga tushadi.
2.3 Aylanish (tick)
Loop aylanishi (loop iteration, tick) — doirani timers'dan close'gacha bir marta aylanib chiqish. Node hujjatlarida "tick" so'zi ko'p uchraydi va ko'pincha shu ma'noda ishlatiladi.
Bir ogohlantirish. process.nextTick nomidagi "tick" — aylanish emas. U hatto fazalarning birortasiga ham tegishli emas: o'z alohida navbati bor va har callback'dan keyin ishlaydi. Node'ning o'z qo'llanmasida ham "nomlar chalkash tanlangan" deyilgan. Buni keyingi darsda — Microtask navbati, process.nextTick va setImmediate — to'liq ochamiz. Bugun microtask va nextTick siz, faqat fazalar bilan ishlaymiz.
Tekshirib ko'ring: Server so'rov kutayotgan, hech qanday taymer yo'q. Event loop vaqtining ko'p qismini qaysi fazada o'tkazadi?
Javob
Poll fazasida. U yangi I/O (so'rov) kelishini kutadi. Taymer va setImmediate yo'q bo'lsa, poll'dan boshqa joyga shoshilishga sabab yo'q — loop shu yerda "uxlab" turadi va so'rov kelishi bilan uning callback'ini bajaradi.
3. Timers fazasi
3.1 Vaqti kelgan taymerlar
Timers fazasida libuv soatga qaraydi va vaqti o'tib bo'lgan taymerlarning callback'larini bajaradi. Vaqti kelmaganlari keyingi aylanishni kutadi.
Taymerlar darsidagi qoida Node'da ham amal qiladi: setTimeout(fn, 100) — "100 ms dan oldin emas", aniq 100 ms emas. Agar o'sha paytda boshqa callback ishlayotgan bo'lsa yoki loop poll'da band bo'lsa, taymer kechikadi.
3.2 setTimeout(fn, 0) aslida 1 ms
Node hujjatida yozilgan: kechikish 1 dan kichik bo'lsa, u 1 ga aylantiriladi. Demak setTimeout(fn, 0) va setTimeout(fn, 1) — Node uchun bir xil taymer. Bir xil vaqtli taymerlar esa qo'yilgan tartibda ishlaydi. Sinab ko'raylik:
setTimeout(() => console.log("1 ms"), 1);
setTimeout(() => console.log("0 ms"), 0);Konsolda:
1 ms
0 ms"1 ms" birinchi chiqdi, chunki u birinchi qo'yilgan — ikkalasi ham 1 ms. Brauzer esa 0 ni 0 deb qoldiradi. Xuddi shu kodni Chrome 155 da ishga tushirdik:
setTimeout(() => console.log("1 ms"), 1);
setTimeout(() => console.log("0 ms"), 0);Chrome konsolida (biz ishga tushirdik, 3 marta bir xil):
0 ms
1 msBu farq kundalik kodda deyarli sezilmaydi. Lekin u bitta muhim narsani ko'rsatadi: Node va brauzer bir xil API nomi bilan har xil qoidaga amal qilishi mumkin. Tartibga bog'liq kod yozsangiz, uni o'zi ishlaydigan muhitda sinang.
4. Poll fazasi — loop'ning kutish joyi
4.1 Poll nima qiladi
Poll (so'rash) fazasi ikki ish qiladi:
- Tayyor bo'lgan I/O hodisalarining callback'larini bajaradi: fayl o'qildi, tarmoqdan javob keldi, yangi ulanish ochildi.
- Ish qolmasa — yangi I/O hodisasini kutadi. Bu kutishda JavaScript ishlamaydi, CPU deyarli band emas.
Blocking va non-blocking I/O darsida ko'rgan readFile callback'i aynan shu fazada ishlaydi. Server so'rovlari ham shu yerga keladi — shuning uchun poll'ni event loop'ning yuragi deyishadi.
4.2 Poll qancha kutadi?
Poll ko'r-ko'rona kutmaydi. libuv kutish vaqtini doiradagi boshqa ishlarga qarab hisoblaydi (libuv hujjati, "Design overview"):
| Holat | Poll qancha kutadi |
|---|---|
setImmediate navbatda bor |
umuman kutmaydi (0) — check fazasi kutyapti |
| Faol taymerlar bor | eng yaqin taymer vaqtigacha |
Taymer ham, setImmediate ham yo'q, lekin ochiq server bor |
cheksiz — yangi so'rov kelguncha |
| Hech qanday faol ish yo'q | kutmaydi — loop tugaydi |
Oshxonadagidek: oshpaz eshik oldida kutadi, lekin qum soati 5 daqiqadan keyin tugashini biladi. Shuning uchun u 5 daqiqadan ortiq kutmaydi — keyin qum soatiga qaytadi. "Darhol" qog'ozi bo'lsa, umuman kutmaydi.
4.3 Loop qachon tugaydi
Doiraning oxirida bitta savol bor: loop tirikmi (alive)? Loop tirik — unda hali kutadigan narsa bor: faol taymer, ochiq server yoki tugallanmagan fayl so'rovi. Hech narsa qolmasa, loop to'xtaydi va Node jarayoni o'zi tugaydi.
Shuning uchun oddiy skript oxiriga yetgach yopiladi, server esa yopilmaydi — server doim yangi ulanishni kutadi. Buni ko'rish uchun beforeExit hodisasidan foydalanamiz. U loop bo'shab, jarayon tugashidan oldin bir marta keladi (process.on — process obyekti darsida chuqur):
process.on("beforeExit", () => console.log("loop bo'sh"));
setTimeout(() => console.log("taymer 50 ms"), 50);
console.log("skript tugadi");Konsolda:
skript tugadi
taymer 50 ms
loop bo'shSkript tugadi, lekin jarayon tugamadi: 50 ms li taymer loop'ni tirik ushlab turdi. Taymer ishlagach, loop'da hech narsa qolmadi — beforeExit keldi va jarayon yopildi.
Taymer loop'ni tirik ushlab turmasligi ham mumkin. unref() — "bu taymer uchun jarayonni ushlab turma" degani:
const taymer = setTimeout(() => console.log("taymer"), 1000);
taymer.unref();
console.log("skript tugadi");Konsolda:
skript tugadi"taymer" umuman chiqmadi. Loop'da unref qilingan taymerdan boshqa hech narsa yo'q edi — jarayon darhol tugadi. Biz o'lchadik: 1 soniya emas, 0.2 soniya. unref va ref ni timers va timers/promises chuqur darsida ishlatamiz: masalan, "har 10 soniyada log yoz, lekin faqat shu sabab bilan dasturni ochiq ushlama".
Tekshirib ko'ring: Skriptda
setInterval(() => console.log("tik"), 1000)bor va boshqa hech narsa yo'q. Jarayon qachon tugaydi?
Javob
Hech qachon — o'zidan tugamaydi. setInterval faol taymer bo'lib qoladi va loop har doim tirik. To'xtatish uchun clearInterval chaqirish, taymerni unref() qilish yoki jarayonni tashqaridan to'xtatish (Ctrl+C) kerak.
5. Check fazasi va setImmediate
5.1 setImmediate — "poll'dan keyin, darhol"
setImmediate(fn) — callback'ni check fazasiga qo'yadi. Check har aylanishda poll'dan keyin keladi. Nomi "darhol" bo'lsa ham, u darhol emas: avval joriy kod tugaydi, keyin loop check fazasigacha yetadi.
setImmediate faqat Node'da (va Node'ga o'xshash runtime'larda) bor. Chrome konsolida typeof setImmediate — "undefined".
5.2 I/O callback ichida: check doim birinchi
Kodni qadamma-qadam bajarib ko'ring. Har qadamda navbatlarga qarang: callback qaysi fazaning navbatiga tushdi?
Xuddi shu kod oddiy blokda — o'z kompyuteringizda fazalar.mjs deb saqlab, node fazalar.mjs bilan ishga tushiring:
import { readFile } from "node:fs";
readFile(import.meta.filename, () => {
setTimeout(() => console.log("timers: setTimeout"), 0);
setImmediate(() => console.log("check: setImmediate"));
console.log("poll: fayl o'qildi");
});Konsolda:
poll: fayl o'qildi
check: setImmediate
timers: setTimeoutBiz bu faylni 30 marta ishga tushirdik — 30 marta ham aynan shu tartib. Bu tasodif emas, kafolat: poll'dan check'ga yo'l timers'dan o'tmaydi.
5.3 Taymer ichida: yana check birinchi
Endi ikkalasini taymer callback'i ichida qo'yamiz. Biz timers fazasidamiz:
setTimeout(() => {
setTimeout(() => console.log("setTimeout 0"), 0);
setImmediate(() => console.log("setImmediate"));
}, 0);Konsolda:
setImmediate
setTimeout 0Doiraga qarang: timers → pending → idle/prepare → poll → check → close → timers. Check shu aylanishning o'zida keladi, yangi taymer esa keyingi aylanishni kutadi. Poll ham bu yerda kutib o'tirmaydi — navbatda setImmediate bor (poll kutish jadvalidagi birinchi qator). 30 ta ishga tushirishdan 30 tasida shu tartib chiqdi.
5.4 Faylning yuqori darajasida: kafolat yo'q
Endi eng mashhur holat — ikkalasi faylning yuqori darajasida:
setTimeout(() => process.stdout.write("T"), 0);
setImmediate(() => process.stdout.write("I"));process.stdout.write — console.log ga o'xshaydi, lekin yangi qatorsiz yozadi: IT yoki TI chiqadi. Natija oldindan ma'lum emas, shuning uchun bu blokning chiqishini yozmadik — o'lchadik:
- ti.mjs, 100 marta92 IT, 8 TI92 %
- ti.cjs, 100 marta89 IT, 11 TI89 %
- node -e, 20 martakanon: 19 IT, 1 TI95 %
- oldin 5 ta console.log30 dan 30 TI0 %
Manba: IlmHamroh o'lchovi, 2026-10-23: Node 24.21.0, Windows 11, i5-12500H; har safar yangi jarayon
Nimaga qarang: oxirgi ustun. Kod oldidan bir nechta console.log qo'shganimizda setTimeout 30 martadan 30 marta birinchi bo'ldi. Sababi — o'sha "1 ms":
- Skript tugagach, loop timers fazasiga keladi va soatga qaraydi.
- Skript ishga tushganidan beri 1 ms o'tgan bo'lsa — taymer tayyor,
Tbirinchi. - 1 ms hali o'tmagan bo'lsa — taymer o'tkazib yuboriladi, loop poll va check'ga o'tadi,
Ibirinchi.
Ya'ni tartib skript qancha ishlaganiga va kompyuter qanchalik bandligiga bog'liq. Bunday tartibga hech qachon tayanmang. Qaysi biri birinchi bo'lishi muhim bo'lsa, ikkalasini bitta I/O yoki taymer callback'i ichiga qo'ying — u yerda tartib kafolatlangan.
Tekshirib ko'ring:
setImmediateichida yanasetImmediateqo'yilsa, ichkisi shu check fazasida ishlaydimi yoki keyingi aylanishdami?
Javob
Keyingi aylanishda. Check fazasi boshida navbatda turganlarni bajaradi; ular ishlayotganda qo'shilganlari keyingi aylanishning check fazasiga qoladi. Shuning uchun setImmediate zanjiri loop'ni qotirmaydi — har aylanishda taymerlar va I/O'ga ham navbat tegadi. Bu xususiyat og'ir ishni bo'laklashda ishlatiladi (Event loop'ni bloklash).
6. Qolgan fazalar: pending, idle/prepare, close
6.1 Pending callbacks
Pending callbacks (kechiktirilgan callback'lar) — oldingi aylanishda bajarib ulgurilmagan ba'zi tizim I/O callback'lari. Node hujjati misol qilib TCP xatolarini keltiradi: ba'zi tizimlar ulanish xatosini (ECONNREFUSED) darhol emas, shu fazada xabar qiladi. Bu fazani kodingizdan boshqara olmaysiz va deyarli o'ylamaysiz.
6.2 Idle va prepare
Idle, prepare — libuv va Node'ning ichki ishlari. Prepare — poll kutishga kirishidan oldin bajariladigan tayyorgarlik. Ularga JavaScript'dan callback qo'yib bo'lmaydi; ro'yxatda ular faqat doirani to'liq ko'rsatish uchun.
6.3 Close callbacks
Close callbacks (yopilish callback'lari) — resurs to'satdan yopilganda keladigan "close" hodisalari, masalan tarmoq ulanishi (socket.destroy()). Ulanish va hodisalar bilan EventEmitter asoslari va keyingi tarmoq darslarida ishlaymiz.
Bu fazaning tartibiga ham tayanmang. Biz I/O callback ichida socket'ni yopib, yoniga setTimeout(0) va setImmediate qo'ydik va 30 marta ishga tushirdik. 29 marta "close" taymerdan keyin keldi, 1 marta — oldin. Sabab: Windows'da ulanish yopilishi alohida asinxron ish, u bir necha aylanish olishi mumkin. Linux'da sinamadik; tartib u yerda boshqacha bo'lishi mumkin. Qoida bir xil: "close" hodisasi "qachondir, yopilish tugaganda" keladi.
6.4 libuv hujjati va Node qo'llanmasi
Node qo'llanmasi doirani timers'dan boshlaydi — biz ham shunday chizdik. libuv hujjatida esa taymerlar har aylanish oxirida, close'dan keyin ishlaydi. Bu bitta doiraning ikki xil chizilishi: tartib ham, bugungi qoidalar ham o'zgarmaydi.
7. Brauzer bilan taqqoslash
Event loop darsidagi jadvalni endi to'liq qilamiz:
| Brauzer | Node | |
|---|---|---|
| Task navbati | asosiy navbat (+ ustuvorliklar) | fazalar: timers, poll, check … |
setTimeout(fn, 0) |
0 ms (ichma-ich 5 dan keyin 4 ms) | 1 ms |
setImmediate |
yo'q | check fazasi |
| Ekranni chizish | bor | yo'q |
| Loop tugaydimi | sahifa yopilguncha yashaydi | ish qolmasa jarayon tugaydi |
Umumiy qism ham katta. Microtask navbati ikkala joyda ham har callback'dan keyin to'liq bo'shatiladi. Taymer ichidagi Promise ikkala muhitda bir xil tartib beradi:
setTimeout(() => {
console.log("taymer");
Promise.resolve().then(() => console.log("microtask"));
}, 0);
console.log("sinxron");Konsolda:
sinxron
taymer
microtaskSaytda «Ishga tushir» bilan sinang — brauzer ham, Node 24 ham shu tartibni beradi.
8. CLI qadami: valyuta da fazalar
Bu darsda valyuta ga kod qo'shilmaydi va commit yo'q — loyiha process obyekti darsida boshlanadi. Yakuniy valyuta bitta buyruqni bajarganda loop'da nima bo'ladi:
| Kodda | Faza |
|---|---|
fetch bilan CBU kurslarini so'rash — javob kutish |
poll |
| keshni fayldan o'qish va yozish | poll (fayl ishi thread pool'da, callback — poll'da) |
qayta urinish oldidan kutish (setTimeout) |
timers |
| javob chiqdi, ish qolmadi | loop bo'sh → jarayon tugaydi |
Kanon kodini qidirdik: 19 ta manba faylida ham, testlarda ham birorta setImmediate yo'q. CLI bitta buyruqni bajaradi — ishni keyingi aylanishga qoldirish ehtiyoji yo'q. CLI uchun muhim fakt boshqa: ish tugagach jarayon o'zi yopilishi kerak. Yopilmay qolgan taymer yoki ochiq ulanish valyuta kurs USD ni javobdan keyin ham osiltirib qo'yadi. Kanonda bunga o'xshash holat testlarda chiqdi: kechikkan ulanish soxta serverning close() ini ushlab turdi — buni timers va timers/promises chuqur darsida ko'ramiz.
Kanon tajribasi (t03-tartib) ham shuni berdi: I/O ichida setImmediate → setTimeout 0, yuqori darajada 20 jarayondan 19 tasida setImmediate birinchi.
9. Ko'p uchraydigan xatolar
9.1 "setImmediate — darhol"
Nomi aldaydi. setImmediate joriy kodni to'xtatmaydi va hatto microtask'lardan ham keyin keladi. U "shu aylanishning check fazasida" degani. Haqiqatan eng tez navbat — process.nextTick va microtask'lar (Microtask navbati, process.nextTick va setImmediate).
9.2 Yuqori darajadagi tartibga tayanish
setTimeout(fn, 0) va setImmediate(fn) ni faylning yuqori darajasida yozib, "mening kompyuterimda doim setImmediate birinchi" deb kod yozish — xato. Grafikdagi to'rtinchi ustun ko'rsatdi: bir necha qator qo'shilsa, tartib butunlay teskari bo'ladi. Tartib kerak bo'lsa — ikkalasini bitta callback ichiga qo'ying yoki bittasini boshqasining ichidan chaqiring.
9.3 "Skript tugadi, lekin terminal qaytmayapti"
Dastur natija chiqardi, lekin jarayon tugamaydi. Sabab deyarli doim — loop'ni tirik ushlab turgan narsa: setInterval, ochiq server, yopilmagan ulanish. Yechim: uni yoping (clearInterval, server.close()) yoki ataylab ushlab turmaslik kerak bo'lsa, unref() qiling. process.exit() bilan "kesib tashlash" — oxirgi chora: u yozilmay qolgan chiqishni yo'qotishi mumkin (process obyekti).
9.4 "Taymer aniq vaqtida ishlaydi"
setTimeout(fn, 100) — kamida 100 ms. Loop og'ir sinxron hisob bilan band bo'lsa, taymer kechikadi: kanonda bitta sikl uni 1 390 ms gacha kechiktirdi (Event loop'ni bloklash).
9.5 Brauzerda setImmediate yoki process
Node uchun yozilgan tartib misolini brauzerga ko'chirsangiz:
Uncaught ReferenceError: setImmediate is not definedTarjimasi: "ushlanmagan xato: setImmediate aniqlanmagan". Brauzerda check fazasi ham, setImmediate ham yo'q. Brauzer uchun "keyingi task'da" kerak bo'lsa — setTimeout(fn, 0).
10. Mashqlar
Mashqlar kurs/mashqlar/24/03-event-loop-fazalari/ da. Har kodni .mjs faylga saqlab, node fayl.mjs bilan tekshiring.
1-mashq (oson): fazalarni joyiga qo'ying
setImmediatecallback'lari ishlaydigan faza — .readFilecallback'i ishlaydigan faza — .- Vaqti kelgan
setTimeoutcallback'lari — . - Socket yopilganda
"close"hodisasi — . - Node'da
setTimeout(fn, 0)ning haqiqiy kechikishi, ms — .
Yechim
check — poll'dan keyingi faza, setImmediate uchun. poll — fayl va tarmoq callback'lari. timers — vaqti o'tgan taymerlar. close — yopilish hodisalari. 1 — Node hujjati bo'yicha 1 dan kichik kechikish 1 ga aylanadi.
2-mashq (o'rta): tartibni oldindan ayting
Har kod nima chiqarishini ishga tushirmasdan yozing. Agar tartib kafolatlanmagan bo'lsa — shunday deb yozing. Keyin ishga tushirib tekshiring (uchinchisini 10 marta).
A:
import { readFile } from "node:fs";
readFile(import.meta.filename, () => {
setImmediate(() => console.log("A1"));
setTimeout(() => console.log("A2"), 0);
setImmediate(() => console.log("A3"));
});B:
setTimeout(() => {
console.log("B1");
setImmediate(() => console.log("B2"));
setTimeout(() => console.log("B3"), 0);
}, 0);C:
setImmediate(() => console.log("C1"));
setTimeout(() => console.log("C2"), 0);Yechim
A — A1, A3, A2. Ikkala setImmediate shu aylanishning check fazasida, qo'yilgan tartibda. Taymer keyingi aylanishda.
import { readFile } from "node:fs";
readFile(import.meta.filename, () => {
setImmediate(() => console.log("A1"));
setTimeout(() => console.log("A2"), 0);
setImmediate(() => console.log("A3"));
});Konsolda:
A1
A3
A2B — B1, B2, B3. B1 — taymer callback'ining o'zi. Undan keyin loop shu aylanishning check fazasiga yetadi (B2), yangi taymer esa keyingi aylanishda (B3).
setTimeout(() => {
console.log("B1");
setImmediate(() => console.log("B2"));
setTimeout(() => console.log("B3"), 0);
}, 0);Konsolda:
B1
B2
B3C — kafolatlanmagan. Faylning yuqori darajasida taymer 1 ms bo'lgani uchun natija skript qancha vaqt ishlaganiga bog'liq. 10 marta ishga tushirsangiz, ko'pincha C1 birinchi, ba'zan C2 — sizning kompyuteringizda nisbat boshqacha bo'lishi mumkin.
3-mashq (qiyin): keyingiAylanish() yordamchisi
await bilan "loop'ning keyingi check fazasigacha kut" deyish uchun funksiya yozing: keyingiAylanish() Promise qaytarsin va u setImmediate orqali bajarilsin. Ishora: Promise asoslari dagi new Promise((resolve) => ...) va setImmediate(resolve) yetadi.
Keyin uni I/O callback'i ichida tekshiring: callback'da avval setTimeout(() => console.log("taymer"), 0) qo'ying, keyin await keyingiAylanish() qilib "keyingi aylanish" deb yozing. Qaysi biri birinchi chiqadi va nega?
Yechim
import { readFile } from "node:fs";
function keyingiAylanish() {
return new Promise((resolve) => setImmediate(resolve));
}
readFile(import.meta.filename, async () => {
setTimeout(() => console.log("taymer"), 0);
console.log("poll: callback");
await keyingiAylanish();
console.log("keyingi aylanish");
});Konsolda:
poll: callback
keyingi aylanish
taymersetImmediate(resolve) check fazasida ishlaydi va Promise'ni bajaradi. await dan keyingi kod — microtask: u o'sha setImmediate callback'idan keyin darhol ishlaydi, hali check fazasida. Taymer esa keyingi aylanishning timers fazasini kutadi. async callback'ni readFile ga berish mumkin: readFile qaytgan Promise'ga qaramaydi, faqat funksiyani chaqiradi. Bu yordamchini Event loop'ni bloklash darsida uzun hisobni bo'laklashda ishlatamiz: har bo'lakdan keyin await keyingiAylanish() — loop boshqa ishlarga ham vaqt topadi.
4-mashq: CLI qadami — tartibni o'z kompyuteringizda o'lchang
valyuta ga bu darsda kod qo'shilmaydi. Kanon tajribasini o'zingizda takrorlang:
ti.mjsfayliga «Faylning yuqori darajasida: kafolat yo'q» bo'limidagi ikki qatorni yozing (setTimeoutvasetImmediate,process.stdout.writebilan).- Uni 20 marta ishga tushiring. Git Bash'da:
for i in $(seq 20); do node ti.mjs; echo; done. Necha martaIT, necha martaTIchiqdi? - Fayl boshiga 5 ta
console.log("salom")qo'shib, yana 20 marta ishga tushiring. Nima o'zgardi?
Yechim
Kanon mashinasida (Node 24.21.0, Windows 11, i5-12500H) 100 dan 92 marta IT chiqdi, 5 ta console.log bilan esa 30 dan 30 marta TI. Sizda raqamlar boshqacha bo'lishi mumkin — xulosa raqamda emas, "tartib kafolatlanmagan" qoidasida. console.log vaqt oladi: loop timers fazasiga yetganda 1 ms o'tib bo'lgan, taymer birinchi chiqadi.
11. Real ishda
- Intervyu: "Node event loop fazalarini sanab bering", "
setImmediatevasetTimeout(0)farqi?", "Poll fazasi nima qiladi?" — Node backend intervyularining doimiy savollari. Yaxshi javob fazalarni tartib bilan aytadi va "I/O ichida kafolatlangan, yuqori darajada yo'q" farqini ko'rsatadi. - Serverlar: Express, Fastify, NestJS so'rovlari poll fazasida keladi. Bitta callback uzoq ishlasa, boshqa so'rovlar va taymerlar kutadi — shuning uchun server kodida sinxron og'ir ish taqiqlanadi (Event loop'ni bloklash).
- Osilib qolgan jarayonlar: testlar tugagach jarayon yopilmasa (yopilmagan server, ulanish,
setInterval), test yurgizgich ogohlantiradi yoki kutib qoladi. Sababini topish — "loop'ni nima tirik ushlab turibdi?" savoli.
Xulosa
- Node event loop — fazalar doirasi: timers → pending → idle/prepare → poll → check → close.
- Poll — kutish joyi:
setImmediatebo'lsa kutmaydi, taymer bo'lsa eng yaqin taymergacha, ochiq server bo'lsa cheksiz. - I/O yoki taymer callback'i ichida
setImmediatedoimsetTimeout(0)dan oldin; yuqori darajada tartib kafolatlanmagan (biz 92/8 va 0/30 ni o'lchadik). - Node'da
setTimeout(fn, 0)— aslida 1 ms; brauzerda — 0 ms. - Loop'da faol ish qolmasa jarayon tugaydi;
setInterval, ochiq server uni tirik ushlab turadi,unref()— ushlab turma degani.
Keyingi dars: Microtask navbati, process.nextTick va setImmediate — microtask'lar fazalar orasida qayerda ishlaydi, process.nextTick nega .cjs faylda Promise'dan oldin, .mjs da esa keyin.
Manbalar
- Node.js qo'llanmasi — The Node.js Event Loop: https://nodejs.org/en/learn/asynchronous-work/event-loop-timers-and-nexttick
- libuv hujjati — Design overview (The I/O loop): https://docs.libuv.org/en/v1.x/design.html
- Node.js API — Timers (
setTimeout,setImmediate,unref): https://nodejs.org/api/timers.html
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!