Mundarija (24)
- Bu darsda
- 1. Nega bu kerak?
- 2. Mexanizmni o'zimiz yozamiz
- 3. Haqiqiy EventEmitter
- 3.1 Asosiy metodlar
- 3.2 Tinglovchi ichida this
- 4. Tinglovchilar sinxron chaqiriladi
- 4.1 Tartibni ko'ramiz
- 4.2 Bundan kelib chiqadigan uchta natija
- 5. "error" hodisasi — maxsus
- 5.1 Tinglovchi bo'lmasa — throw
- 5.2 To'g'ri ushlash
- 6. O'z hodisali klassingiz
- 7. Hodisa, callback yoki Promise?
- 8. CLI qadami: holat yo'q — emitterlar ichkarida
- 9. Ko'p uchraydigan xatolar
- 10. Mashqlar
- 1-mashq (oson): natijani ayting
- 2-mashq (o'rta): once ni o'zingiz yozing
- 3-mashq (qiyin): buyurtmalar navbati
- 4-mashq: CLI qadami — emitterlarni toping
- 11. Real ishda
- Xulosa
- Manbalar
Node.js EventEmitter: on, once, emit va maxsus "error" hodisasi
Qisqacha:
EventEmitter(node:events) — Node'dagi hodisalar mexanizmi: kimdiremit("nom", ma'lumot)qiladi,on("nom", funksiya)bilan ro'yxatdan o'tganlar chaqiriladi. Soket,readline, stream, server,process— hammasi EventEmitter. Ikkita muhim qoida: tinglovchilar sinxron, qo'shilgan tartibda chaqiriladi;"error"hodisasining tinglovchisi bo'lmasa,emit("error")xatoni tashlaydi va jarayon yiqiladi.
Bu darsda
- Hodisa mexanizmini o'zingiz 15 qatorda yozasiz va uning ichini tushunasiz.
on,once,emit,offvalistenerCountbilan ishlaysiz.- Tinglovchilar nega sinxron chaqirilishini va bundan kelib chiqadigan tuzoqlarni bilasiz.
"error"hodisasining maxsusligini va uni qanday to'g'ri ushlashni tushuntirasiz.EventEmitterdan meros olib, o'z hodisali klassingizni yozasiz.
Oldin bilishingiz kerak: addEventListener asoslari, Listener opsiyalari: once, passive, signal, ES Modules Node'da, net, dns va tls.
1. Nega bu kerak?
Oxirgi darslarni eslang. readline da rl.on("line", …) va rl.on("close", …) yozdik. child_process da child.on("close", …) va child.on("message", …). net da socket.on("error", …) — usiz server yiqilardi. Hammasida bir xil on. Bu tasodif emas: ularning hammasi bitta klassdan — EventEmitter dan kelib chiqqan.
Node'ning ko'p qismi "biror narsa bo'lganda — xabar ber" g'oyasiga qurilgan. Fayl o'qildi, ulanish keldi, qator tugadi, jarayon tugadi — bu hodisalar. Kimdir hodisani e'lon qiladi, boshqalar unga obuna bo'ladi. Brauzerda buni addEventListener bilan ko'rgansiz (addEventListener asoslari). Node'da DOM yo'q, lekin g'oya aynan o'sha.
O'xshatish: Telegram kanal. Kanal egasi post qo'yadi (emit), obunachilar xabar oladi (on). Egasi kim obuna bo'lganini bilishi shart emas. Obunachi xohlasa — chiqib ketadi (off), yoki bitta postni o'qib, keyin chiqadi (once).
Bu naqshning nomi — kuzatuvchi (observer). U ikki tomonni bir-biridan ajratadi: soket ma'lumot kelganini bildiradi, lekin uni kim va qanday ishlatishini bilmaydi.
2. Mexanizmni o'zimiz yozamiz
EventEmitter ichida sehr yo'q. Uning yuragi — "hodisa nomi → funksiyalar ro'yxati" jadvali. Keling, uni o'zimiz yozamiz. Bu blok toza JavaScript — saytda «Ishga tushir» bilan ham ishlaydi:
class TinyEmitter {
#listeners = new Map(); // nom → funksiyalar massivi
on(name, fn) {
const list = this.#listeners.get(name) ?? [];
list.push(fn);
this.#listeners.set(name, list);
return this;
}
off(name, fn) {
const list = this.#listeners.get(name) ?? [];
this.#listeners.set(name, list.filter((f) => f !== fn));
return this;
}
emit(name, ...args) {
const list = this.#listeners.get(name) ?? [];
for (const fn of [...list]) fn.apply(this, args);
return list.length > 0;
}
}
const channel = new TinyEmitter();
const ali = (post) => console.log("Ali o'qidi:", post);
channel.on("post", ali);
channel.on("post", (post) => console.log("Malika o'qidi:", post));
channel.emit("post", "Dollar kursi 11 903");
channel.off("post", ali);
console.log(channel.emit("post", "Yevro kursi 13 372"));
console.log(channel.emit("reklama", "..."));Konsolda:
Ali o'qidi: Dollar kursi 11 903
Malika o'qidi: Dollar kursi 11 903
Malika o'qidi: Yevro kursi 13 372
true
falseHammasi shu: on — ro'yxatga qo'shish, off — ro'yxatdan olib tashlash, emit — ro'yxatdagi hammasini navbat bilan, shu zahoti chaqirish. emit tinglovchi bor-yo'qligini qaytaradi: "reklama"ga hech kim obuna emas — false.
Ikki nozik joy:
offuchun aynan o'sha funksiya kerak. Shuning uchun Ali tinglovchisialinomli o'zgaruvchiga saqlandi. Malikaniki — nomsiz strelka funksiya, uni endi olib tashlab bo'lmaydi.for (const fn of [...list])— nusxa ustida aylanamiz. Agar tinglovchi o'zinioffqilsa, ro'yxat aylanish paytida o'zgarmasin. HaqiqiyEventEmitterham shunday qiladi.
Tekshirib ko'ring:
channel.on("post", ali)ikki marta chaqirilsa, bittaemitda Ali necha marta o'qiydi?
Javob
Ikki marta. Ro'yxatga bir xil funksiya ikki marta qo'shildi, emitter takrorni tekshirmaydi. Haqiqiy EventEmitter ham xuddi shunday — brauzerdagi addEventListener dan farqli (u bir xil funksiyani ikkinchi marta qo'shmaydi).
3. Haqiqiy EventEmitter
3.1 Asosiy metodlar
Endi Node'niki. Bu va keyingi bloklar node:events ni ishlatadi — ular faqat Node'da ishlaydi, o'z terminalingizda node fayl.mjs bilan sinang:
import { EventEmitter } from "node:events";
const rates = new EventEmitter();
rates.on("ozgardi", (code, rate) => console.log(`${code}: ${rate}`));
rates.once("ozgardi", () => console.log("(birinchi o'zgarish)"));
console.log(rates.emit("ozgardi", "USD", 11903.15));
console.log(rates.emit("ozgardi", "EUR", 13372.4));
console.log(rates.listenerCount("ozgardi"), rates.eventNames());Konsolda:
USD: 11903.15
(birinchi o'zgarish)
true
EUR: 13372.4
true
1 [ 'ozgardi' ]| Metod | Nima qiladi |
|---|---|
on(nom, fn) (= addListener) |
tinglovchi qo'shadi |
once(nom, fn) |
faqat birinchi emit da, keyin o'zi o'chadi |
off(nom, fn) (= removeListener) |
bitta tinglovchini olib tashlaydi |
emit(nom, ...args) |
hammasini chaqiradi; tinglovchi bo'lsa true |
removeAllListeners(nom) |
nomdagi hammasini olib tashlaydi |
listenerCount(nom), eventNames() |
nechta tinglovchi, qaysi nomlar bor |
emit ga istalgancha argument berish mumkin — ular tinglovchiga shu tartibda keladi (code, rate). Brauzer dispatchEvent dan farqi shu: u yerda bitta event obyekti, bu yerda — oddiy argumentlar.
3.2 Tinglovchi ichida this
Oddiy function tinglovchida this — emitterning o'zi. Strelka funksiyada esa this tashqaridan olinadi (this darsidagi qoida):
import { EventEmitter } from "node:events";
const e = new EventEmitter();
e.on("x", function () {
console.log("function:", this === e);
});
e.on("x", () => console.log("strelka:", this));
e.emit("x");Konsolda:
function: true
strelka: undefinedES modulning yuqori darajasida this — undefined. Amalda this ga tayanish kam; kerakli obyektni yopilish (closure) orqali olgan ma'qul.
4. Tinglovchilar sinxron chaqiriladi
4.1 Tartibni ko'ramiz
Bu darsning eng muhim qoidasi. "Hodisa" so'zi asinxronlikni eslatadi, lekin emit — oddiy funksiya chaqiruvi:
import { EventEmitter } from "node:events";
const e = new EventEmitter();
e.on("buyurtma", (dish) => console.log("1-tinglovchi:", dish));
e.on("buyurtma", (dish) => console.log("2-tinglovchi:", dish));
console.log("emit oldidan");
e.emit("buyurtma", "osh");
console.log("emit dan keyin");Konsolda:
emit oldidan
1-tinglovchi: osh
2-tinglovchi: osh
emit dan keyin"emit dan keyin" ikkala tinglovchidan keyin chiqdi. emit hamma tinglovchini tugatmaguncha qaytmaydi. Event loop, navbat, microtask — hech biri ishtirok etmaydi.
Qadamlarda stekka qarang: tinglovchilar emit ning ichida — xuddi oddiy funksiya chaqiruvidek. Vizual sodda model bilan qurilgan (EventEmitter ning o'zi emas), lekin tartib bir xil.
4.2 Bundan kelib chiqadigan uchta natija
Birinchi: sekin tinglovchi hammani to'xtatadi. Bitta tinglovchi 300 ms hisoblasa, emit ham 300 ms "qotadi", undan keyingi tinglovchilar ham kutadi. Va bu vaqtda event loop ham bloklangan (Event loop'ni bloklash).
Ikkinchi: tinglovchidagi xato emit ga uchadi. Tinglovchi throw qilsa, xato emit ni chaqirgan joyga chiqadi va qolgan tinglovchilar chaqirilmaydi.
Uchinchi: konstruktor ichidagi emit yo'qoladi. Klassik xato:
import { EventEmitter } from "node:events";
class Loader extends EventEmitter {
constructor() {
super();
this.emit("tayyor"); // ❌ hali hech kim tinglamayapti
}
}
const loader = new Loader();
loader.on("tayyor", () => console.log("eshitildi"));
console.log("tinglovchilar:", loader.listenerCount("tayyor"));Konsolda:
tinglovchilar: 1"eshitildi" chiqmadi: emit konstruktorda sinxron bajarildi, on esa keyinroq qo'shildi. Hodisa havoga ketdi. Tuzatish — emit ni joriy kod tugagandan keyinga surish:
import { EventEmitter } from "node:events";
class Loader extends EventEmitter {
constructor() {
super();
process.nextTick(() => this.emit("tayyor")); // ✅
}
}
const loader = new Loader();
loader.on("tayyor", () => console.log("eshitildi"));Konsolda:
eshitildiprocess.nextTick callback'i joriy sinxron kod tugagach, lekin boshqa har qanday ishdan oldin bajariladi (Microtask, process.nextTick va setImmediate). Bu vaqtgacha on allaqachon qo'shilgan. Node'ning o'z modullari ham aynan shunday qiladi.
Tekshirib ko'ring: Uchta tinglovchi bor, ikkinchisi
throw new Error("x")qiladi.emitnitry/catchga o'radik. Uchinchi tinglovchi chaqiriladimi?
Javob
Yo'q. Xato ikkinchi tinglovchidan emit orqali catch ga uchadi va emit ichidagi sikl to'xtaydi. Birinchisi bajarilgan, uchinchisi — yo'q. Shuning uchun tinglovchilar ichida xatoni o'zingiz ushlang yoki xatoni "error" hodisasi orqali bildiring.
5. "error" hodisasi — maxsus
5.1 Tinglovchi bo'lmasa — throw
Boshqa har qanday hodisa tinglovchisiz emit qilinsa, hech narsa bo'lmaydi (false qaytadi). "error" esa boshqacha:
import { EventEmitter } from "node:events";
const e = new EventEmitter();
console.log("oldin");
e.emit("error", new Error("CBU javob bermadi"));
console.log("keyin");Konsolda:
oldin
Error: CBU javob bermadi"keyin" chiqmadi — jarayon yiqildi. Terminalda to'liq iz ko'rinadi, oxirida Node Emitted 'error' event at: qatorini qo'shadi ("error hodisasi shu yerda chiqarilgan"). Kanon tajribasida ham xuddi shu (t27-events.txt: "error tinglovchisiz → throw").
Nega bunday? Node falsafasi: xato jimgina yo'qolmasin. Soket uzildi, fayl o'qilmadi — agar buni hech kim eshitmasa, dastur noto'g'ri holatda davom etardi. Undan ko'ra baland ovozda yiqilgan yaxshi. net darsida ECONNRESET serverni aynan shu sabab bilan yiqitdi.
Error emas, oddiy satr bilan emit("error", "matn") qilinsa, Node uni o'raydi: ERR_UNHANDLED_ERROR, xabar Unhandled error. ('matn').
5.2 To'g'ri ushlash
Bitta qoida: xato chiqarishi mumkin bo'lgan har emitterga "error" tinglovchisi.
import { EventEmitter } from "node:events";
const e = new EventEmitter();
e.on("error", (err) => console.log("ushlandi:", err.message));
e.emit("error", new Error("CBU javob bermadi"));
console.log("dastur davom etyapti");Konsolda:
ushlandi: CBU javob bermadi
dastur davom etyaptitry/catch emitter xatosi uchun odatda ishlamaydi: xato ko'pincha keyinroq, boshqa event loop aylanishida chiqadi — try bloki allaqachon tugagan bo'ladi. Shuning uchun Node'da "soket, stream, server — har biriga "error" tinglovchisi" degan odat bor.
Diqqat:
process.on("uncaughtException")— hamma ushlanmagan xatolarni tutadi, lekin uni "yiqilmaslik uchun" ishlatmang. Xatodan keyin dastur holati noma'lum. U faqat log yozib, jarayonni to'xtatish uchun (Ushlanmagan xatolar).
6. O'z hodisali klassingiz
EventEmitter dan meros olib, istalgan obyektni hodisa manbaiga aylantirish mumkin. valyuta mavzusida — kursni kuzatadigan klass:
import { EventEmitter } from "node:events";
class RateWatcher extends EventEmitter {
#last = new Map();
update(code, rate) {
const old = this.#last.get(code);
this.#last.set(code, rate);
if (old === undefined) return;
if (rate !== old) this.emit("change", { code, old, rate });
if (Math.abs(rate - old) / old > 0.01) {
this.emit("jump", code);
}
}
}
const watcher = new RateWatcher();
watcher.on("change", ({ code, old, rate }) =>
console.log(`${code}: ${old} → ${rate}`),
);
watcher.on("jump", (code) =>
console.log(`${code}: 1% dan ko'p sakradi!`),
);
watcher.update("USD", 11903.15);
watcher.update("USD", 11965.51);
watcher.update("USD", 11965.51);
watcher.update("USD", 12200);Konsolda:
USD: 11903.15 → 11965.51
USD: 11965.51 → 12200
USD: 1% dan ko'p sakradi!RateWatcher faqat "nima bo'ldi" ni biladi. Telegram'ga yuborish, logga yozish, ekranga chiqarish — tinglovchilarning ishi. Ertaga yangi tinglovchi qo'shsangiz, klass o'zgarmaydi. Hodisa nomlari — oddiy satr; ularni klass hujjatida (JSDoc yoki TypeScript turi) yozib qo'ying.
TypeScript'da hodisalar turini ham berish mumkin: class RateWatcher extends EventEmitter<{ change: [Change]; jump: [string] }> — @types/node buni qo'llaydi va noto'g'ri nom yoki argumentni tip xatosi qiladi.
7. Hodisa, callback yoki Promise?
Natijani bildirishning uch yo'lini bilasiz. Qaysi biri qachon to'g'ri?
| Vaziyat | Mos vosita | Misol |
|---|---|---|
| Natija bitta, bir marta keladi | Promise (await) |
fetch, readFile, execFile |
| Natija ko'p marta keladi, oxiri noma'lum | hodisa (on) |
ma'lumot bo'laklari, yangi ulanishlar |
| Bir nechta turli narsa yuz beradi | hodisa (har biriga nom) | soket: "data", "end", "error" |
| Oddiy, bir martalik "tugagach chaqir" | callback (eski API'larda) | fs.readFile(f, cb) |
Asosiy savol — "necha marta?". Kurs bir marta olinsa — Promise. Server kun bo'yi yangi ulanishlarni qabul qilsa — har ulanish uchun Promise yaratib bo'lmaydi, bu hodisa. child_process ikkalasini ham beradi: execFile (bir natija, promisify bilan Promise) va spawn (oqim va hodisalar).
Ikki dunyo orasida ko'prik ham bor. Hodisani bir marta kutish kerak bo'lsa — uni Promise'ga aylantirish mumkin, ko'p marta kutish kerak bo'lsa — for await bilan o'qiladigan oqimga. readline da for await (const qator of rl) aynan shunday: ichida "line" hodisasi, tashqarida — qulay sikl. Bu ko'priklar — events.once va events.on — keyingi darsning mavzusi.
Yana bitta kichik imkoniyat: tinglovchilar qo'shilgan tartibda chaqiriladi, lekin prependListener(nom, fn) tinglovchini ro'yxat boshiga qo'yadi. Kanon tajribasida: avval on bilan 1, keyin prependListener bilan 0 qo'shildi — natija [ 0, 1 ]. Kutubxona yozayotganda "mening tinglovchim foydalanuvchinikidan oldin ishlasin" kerak bo'lsa ishlatiladi. Har yangi tinglovchi qo'shilganda emitter "newListener" hodisasini ham chiqaradi — kim obuna bo'layotganini kuzatish uchun.
Tekshirib ko'ring: Telegram bot har yangi xabar kelganda funksiya chaqirishi kerak. Promise mosmi yoki hodisa?
Javob
Hodisa. Xabarlar ko'p marta keladi va qachon tugashi noma'lum — Promise faqat bir marta bajariladi. Shuning uchun bot kutubxonalarida bot.on("message", …) ko'rinishi odatiy.
8. CLI qadami: holat yo'q — emitterlar ichkarida
valyuta o'zi hech qanday klassni EventEmitter dan meros qilmaydi: CLI'da tashqi hodisa manbai yo'q — buyruq keldi, natija chiqdi, tugadi. Shuning uchun kanon bu darsda kod qo'shmadi (QADAMLAR: "CLI'da hodisa manbai yo'q, readline/stream ichida bor"). Lekin valyuta kodida emitterlar allaqachon ishlayapti:
| Fayl | Emitter | Hodisa |
|---|---|---|
interaktiv.ts |
readline.Interface |
for await ichida "line", "close" |
interaktiv.test.ts |
PassThrough |
chiqish.on("data", …) |
interaktiv.test.ts |
ChildProcess |
bola.on("exit", r) |
bin.ts |
process |
process.once("SIGINT", …) — Ctrl+C da exit 130 |
Kanon tajribasi (natijalar/tajriba/t27-events.txt, Node 24.21.0) bu darsga tegishli qismi:
1) error tinglovchisiz → throw: tinglovchisiz xato
2) await once(e, 'tayyor'): [ 42 ]
6) tinglovchilar tartibi: prependListener: [ 0, 1 ]Birinchi qator — «"error" hodisasi — maxsus» bo'limidagi qoida. Ikkinchisi — once ning promise ko'rinishi, oltinchisi — prependListener (tinglovchini ro'yxat boshiga qo'shish). prependListener ni «Hodisa, callback yoki Promise?» bo'limida ko'rdik; once va qolgan qatorlar (3–5) — keyingi darsning mavzusi. Bu darsda commit yo'q.
9. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
Unhandled 'error' event, jarayon yiqildi |
"error" tinglovchisi yo'q |
obj.on("error", …) |
| Hodisa "eshitilmadi" | emit konstruktorda yoki on dan oldin |
process.nextTick yoki on ni avval qo'ying |
off ishlamadi |
Boshqa (yangi) funksiya berildi | Funksiyani o'zgaruvchiga saqlang |
| Tinglovchi ikki marta ishlaydi | on ikki marta chaqirilgan |
Bir marta qo'shing yoki once |
emit sekin |
Tinglovchida og'ir ish | Ishni bo'laklang yoki keyinga suring |
Hodisa nomi xato ("chage") |
Satr — tip tekshiruvi yo'q | TypeScript hodisa turi |
10. Mashqlar
Mashqlar kurs/mashqlar/24/27-eventemitter/ da.
1-mashq (oson): natijani ayting
import { EventEmitter } from "node:events";
const e = new EventEmitter();
e.once("x", () => console.log("A"));
e.on("x", () => console.log("B"));
e.emit("x");
e.emit("x");
console.log(e.emit("y"));Konsolda nechta qator va qaysilar chiqadi? Javob: (bo'shliq bilan).
Yechim
Konsolda:
A
B
B
falseBirinchi emit — A (once) va B. Ikkinchisi — faqat B, once o'chgan. "y" ga tinglovchi yo'q — false.
2-mashq (o'rta): once ni o'zingiz yozing
«Mexanizmni o'zimiz yozamiz» bo'limidagi TinyEmitter ga once(name, fn) metodini qo'shing: tinglovchi bir marta chaqirilib, keyin o'zini off qilsin. Maslahat: fn ni o'rab oladigan yangi funksiya yarating, u avval off qilsin, keyin fn ni chaqirsin.
Yechim
class TinyEmitter {
#listeners = new Map();
on(name, fn) {
const list = this.#listeners.get(name) ?? [];
this.#listeners.set(name, [...list, fn]);
return this;
}
off(name, fn) {
const list = this.#listeners.get(name) ?? [];
this.#listeners.set(name, list.filter((f) => f !== fn));
return this;
}
once(name, fn) {
const wrapper = (...args) => {
this.off(name, wrapper);
fn.apply(this, args);
};
return this.on(name, wrapper);
}
emit(name, ...args) {
const list = this.#listeners.get(name) ?? [];
for (const fn of [...list]) fn.apply(this, args);
return list.length > 0;
}
}
const shop = new TinyEmitter();
shop.once("open", () => console.log("birinchi mijozga chegirma!"));
shop.on("open", (day) => console.log(`${day}: do'kon ochildi`));
shop.emit("open", "dushanba");
shop.emit("open", "seshanba");Konsolda:
birinchi mijozga chegirma!
dushanba: do'kon ochildi
seshanba: do'kon ochildioff ga fn emas, wrapper beriladi — ro'yxatda aynan u turibdi. emit nusxa ustida aylanadi, shuning uchun wrapper o'zini olib tashlasa ham, shu aylanishdagi keyingi tinglovchi o'tkazib yuborilmaydi. Haqiqiy EventEmitter ham shunday qiladi; u yana rawListeners() orqali o'ralmagan asl funksiyani ham saqlaydi. Bu blok brauzerda ham ishlaydi.
3-mashq (qiyin): buyurtmalar navbati
Kitchen klassini EventEmitter dan meros qilib yozing. order(dish) — buyurtma qabul qiladi va "yangi" hodisasini chiqaradi. Buyurtma bo'sh satr bo'lsa — throw emas, "error" hodisasi (Error("Bo'sh buyurtma")). "tayyor" hodisasi — ready(dish) chaqirilganda. Tinglovchilar: hisoblagich ("yangi" larni sanaydi), "error" ni ekranga chiqaruvchi. Natija: buyurtmalar osh, "", manti; tayyor — osh.
Yechim
import { EventEmitter } from "node:events";
class Kitchen extends EventEmitter {
order(dish) {
if (!dish.trim()) {
this.emit("error", new Error("Bo'sh buyurtma"));
return;
}
this.emit("yangi", dish);
}
ready(dish) {
this.emit("tayyor", dish);
}
}
const kitchen = new Kitchen();
let count = 0;
kitchen.on("yangi", (dish) => {
count++;
console.log(`${count}-buyurtma: ${dish}`);
});
kitchen.on("tayyor", (dish) => console.log(`${dish} tayyor`));
kitchen.on("error", (err) => console.log("xato:", err.message));
for (const dish of ["osh", "", "manti"]) kitchen.order(dish);
kitchen.ready("osh");
console.log("jami:", count);Konsolda:
1-buyurtma: osh
xato: Bo'sh buyurtma
2-buyurtma: manti
osh tayyor
jami: 2"error" tinglovchisini olib tashlab ko'ring — ikkinchi buyurtmada jarayon yiqiladi va manti hech qachon qabul qilinmaydi. Shuning uchun "xato chiqaradigan emitter — "error" tinglovchisi bilan".
4-mashq: CLI qadami — emitterlarni toping
O'z valyuta loyihangizda \.on\(|\.once\( ni qidiring (git grep -nE "\.on\(|\.once\(" src test). Har topilgan joy uchun yozing: qaysi obyekt, qaysi hodisa, "error" tinglovchisi kerakmi. Kod o'zgarmaydi.
Yechim
Holat 24 bo'yicha kutilgan: test/interaktiv.test.ts — chiqish.on("data") (PassThrough), bola.stdout?.on("data") va bola.on("exit") (ChildProcess); test/yordamchi.ts — soxta server (http.createServer ichida hodisalar bor, lekin .on( siz yozilgan). src da bitta joy: bin.ts dagi process.once("SIGINT", …) — "error" kerak emas, process signal hodisasida xato chiqarmaydi. Qolgan src da .on( yo'q: readline for await bilan o'qiladi (iterator ichida tinglovchilarni o'zi qo'yadi va xatoni await ga uzatadi). Shuning uchun for await — emitterni ishonchli o'qish usuli; buni keyingi darsda events.on bilan ko'ramiz.
11. Real ishda
- Node'ning o'zi —
http.Server("request"),net.Socket, stream'lar,process("exit","SIGINT"),child_process— hammasi EventEmitter. Ularni o'qish uchun shu darsdagi bilim yetadi. - Kutubxonalar — Socket.IO, Mongoose,
ws(WebSocket) — hodisalar bilan ishlaydi. NestJS'da hodisalar uchun alohida modul bor (keyingi qismlarda). - Monolit ichida bo'linish — "buyurtma yaratildi" hodisasini chiqarib, email, hisob-faktura va statistika modullarini bir-biridan ajratish.
- Intervyu savollari: "
emitsinxronmi yoki asinxron?" — sinxron; "errorhodisasining tinglovchisi bo'lmasa nima bo'ladi?" —throw, jarayon yiqiladi.
Xulosa
EventEmitter— "nom → tinglovchilar ro'yxati";on,once,off,emit.emitsinxron: tinglovchilar shu zahoti, qo'shilgan tartibda chaqiriladi;emitulardan keyin qaytadi.- Konstruktordagi
emityo'qoladi —process.nextTickbilan keyinga suring. "error"tinglovchisizemitqilinsa —throw; har xato chiqaradigan emitterga"error"tinglovchisi.extends EventEmitter— o'z hodisali klassingiz; ishni tinglovchilarga bo'ling.
Keyingi dars: EventEmitter chuqur — hodisani await qilish (events.once), for await bilan hodisalar oqimi, tinglovchi sizishi (memory leak) ogohlantirishi va brauzerdagi EventTarget.
Manbalar
- Node.js hujjati — Events (
EventEmitter, "Asynchronous vs. synchronous", "Error events",newListener): https://nodejs.org/docs/latest-v24.x/api/events.html - Node.js hujjati —
process.nextTickva "Why use process.nextTick()?": https://nodejs.org/en/learn/asynchronous-work/understanding-processnexttick - Kanon:
natijalar/tajriba/t27-events.txt(Node 24.21.0, Windows 11, 2026-10-23);valyutaholat 24 testlari. Darsdaginode:eventsmisollari — biz ishga tushirdik (Node 24.21.0).
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!