Mundarija (26)
- Bu darsda
- 1. Nega bu kerak?
- 2. events.once: hodisani bir marta kutish
- 2.1 Asosiy ko'rinish
- 2.2 "error" — rad etishga aylanadi
- 2.3 Timeout: biz topgan nozik joy
- 3. events.on: hodisalar oqimi
- 4. async tinglovchi va captureRejections
- 4.1 Muammo: xato "error" ga bormaydi
- 4.2 Yechim: captureRejections
- 4.3 errorMonitor: xatoni kuzatish, lekin ushlamaslik
- 5. Tinglovchi sizishi (leak)
- 5.1 Ogohlantirish
- 5.2 Odatiy sabab
- 5.3 To'g'ri tuzatish
- 6. EventTarget va CustomEvent
- 7. CLI qadami: holat yo'q — kanon tajribasi
- 8. Ko'p uchraydigan xatolar
- 9. Mashqlar
- 1-mashq (oson): to'g'ri vositani tanlang
- 2-mashq (o'rta): birinchisi qaysi?
- 3-mashq (qiyin): kurslar oqimini for await bilan yig'ish
- 4-mashq: CLI qadami — testni once bilan qayta yozing
- 10. Real ishda
- Xulosa
- Manbalar
Node.js EventEmitter chuqur: events.once, events.on, leak ogohlantirishi va EventTarget
Qisqacha: Hodisani Promise'ga aylantirish mumkin:
await once(emitter, "nom")bir marta kutadi,for await (const args of on(emitter, "nom"))esa hodisalarni oqim sifatida o'qiydi. Ikkalasi"error"ni rad etishga aylantiradi va{ signal }bilan bekor qilinadi.asynctinglovchidagi xato"error"ga o'tmaydi — jarayon yiqiladi (captureRejections: truebuni tuzatadi). Bitta hodisaga 11-tinglovchi qo'shilsa —MaxListenersExceededWarning: odatda bu tinglovchi sizishi (leak) belgisi.
Bu darsda
events.oncebilan hodisaniawaitqilasiz va timeout qo'yasiz.events.onbilan hodisalarnifor awaitsiklida o'qiysiz.asynctinglovchidagi xato qayerga ketishini vacaptureRejectionsni bilasiz.- Leak ogohlantirishini o'qib, sababini topasiz va tuzatasiz.
- Node'dagi
EventTargetvaCustomEventniEventEmitterbilan solishtirasiz.
Oldin bilishingiz kerak: EventEmitter asoslari, Asinxron iteratsiya, Bekor qilish: AbortController, timers va timers/promises.
1. Nega bu kerak?
EventEmitter asoslari darsida hodisalarni on bilan tinglashni o'rgandik. Lekin zamonaviy kod async/await bilan yoziladi va callback'lar unga yaxshi qo'shilmaydi. Masalan, "server tinglay boshlaguncha kut, keyin ulan":
server.listen(0, () => {
const port = server.address().port;
const client = net.connect(port, () => {
client.on("data", (d) => {
// ... ichma-ich davom etadi
});
});
});Har qadam ichkariroqqa kiradi — bu callback hell ning o'zi. Undan tashqari, ikkita xavf bor. Birinchisi: "error" hodisasi await va try/catch ga tushmaydi. Ikkinchisi: har chaqiruvda yangi tinglovchi qo'shib, olib tashlashni unutish oson — ular to'planib, xotirani yeydi.
Bu dars uchala muammoni hal qiladi: hodisani await qilish, xatolarni Promise'ga o'tkazish va tinglovchilarni o'z vaqtida tozalash.
O'xshatish: navbat chiptasi. on — tablo ostida turib, har chaqiruvni eshitib turish. once — "mening raqamim chaqirilsa, xabar ber" deb SMS'ga yozilish: bir marta keladi va siz shu orada boshqa ish qilasiz.
2. events.once: hodisani bir marta kutish
2.1 Asosiy ko'rinish
once(emitter, nom) — hodisa bir marta yuz berganda bajariladigan Promise qaytaradi. Natija — emit ga berilgan argumentlar massivi:
import { EventEmitter, once } from "node:events";
const kitchen = new EventEmitter();
setTimeout(() => kitchen.emit("tayyor", "osh", 2), 10);
const [dish, portions] = await once(kitchen, "tayyor");
console.log(dish, portions); // osh 2Massiv bo'lgani uchun destrukturizatsiya bilan olinadi. Bitta argument bo'lsa ham massiv: const [port] = await once(…).
Endi «Nega bu kerak?» dagi kod tekis bo'ladi:
import net from "node:net";
import { once } from "node:events";
const server = net.createServer((s) => s.end("salom\n"));
server.listen(0, "127.0.0.1");
await once(server, "listening");
const client = net.connect(server.address().port, "127.0.0.1");
await once(client, "connect");
const [data] = await once(client, "data");
console.log(String(data).trim()); // salom
server.close();Ichma-ichlik yo'q — kod yuqoridan pastga o'qiladi. net darsidagi new Promise((r) => server.listen(0, host, r)) ning qisqaroq ko'rinishi.
2.2 "error" — rad etishga aylanadi
once kutayotgan paytda emitter "error" chiqarsa, Promise rad etiladi:
import { EventEmitter, once } from "node:events";
const e = new EventEmitter();
setTimeout(() => e.emit("error", new Error("CBU javob bermadi")), 10);
try {
await once(e, "tayyor");
} catch (error) {
console.log("ushlandi:", error.message);
}Konsolda:
ushlandi: CBU javob bermadiEndi oddiy try/catch ishlaydi. once vaqtincha "error" tinglovchisini qo'yadi va ish tugagach ikkalasini ham olib tashlaydi — orqada hech narsa qolmaydi.
2.3 Timeout: biz topgan nozik joy
Hodisa umuman kelmasa, once abadiy kutadi. Shuning uchun { signal } beriladi (timers darsidagi AbortSignal.timeout). Biz shuni sinab ko'rdik va kutilmagan natija oldik (Node 24.21.0):
import { EventEmitter, once } from "node:events";
const e = new EventEmitter(); // hech kim emit qilmaydi
try {
await once(e, "tayyor", { signal: AbortSignal.timeout(20) });
} catch (error) {
console.log(error.name, error.cause?.name);
}Biz ishga tushirgan chiqish:
Warning: Detected unsettled top-level await at file:///C:/Users/…/kutish.mjs:5
await once(e, "tayyor", { signal: AbortSignal.timeout(20) });
^catch ham ishlamadi — jarayon exit kod 13 bilan tugadi (readline darsidagi o'sha ogohlantirish). Sababi: AbortSignal.timeout ning ichki taymeri unref qilingan (timers darsidagi unref). U jarayonni tirik ushlab turmaydi. Emitter ham hech narsa kutmaydi — event loop'da ish qolmadi va Node "osilib qolgan await" deb chiqib ketdi.
Haqiqiy dasturda odatda boshqa ish bor (soket, server, fayl) — ular jarayonni ushlab turadi va timeout kutilgandek ishlaydi. Biz ref qilingan taymer qo'shib qayta sinadik — natija AbortError TimeoutError: xato nomi AbortError, sababi (cause) — TimeoutError, xuddi timers/promises dagidek. Saboq: "faqat timeout signali kutayotgan" holat — odatda testlar va kichik skriptlar — shu tuzoqqa tushadi.
Tekshirib ko'ring:
once(socket, "data")bilan soketdan ma'lumot kutyapsiz va server birdaniga ikki bo'lak yubordi. Ikkinchi bo'lak nima bo'ladi?
Javob
U yo'qolishi mumkin. once birinchi "data" dan keyin tinglovchisini olib tashlaydi; ikkinchi bo'lak kelganda hech kim tinglamaydi. Bir martalik signal uchun ("listening", "connect", "close") once ideal; ko'p marta keladigan ma'lumot uchun — on yoki stream'ni for await bilan o'qish.
3. events.on: hodisalar oqimi
on(emitter, nom) — async iterator qaytaradi (Asinxron iteratsiya). Har emit — sikldagi bitta qadam. Kelgan hodisalar navbatda saqlanadi, sekin o'qisangiz ham yo'qolmaydi:
import { EventEmitter, on } from "node:events";
const rates = new EventEmitter();
const controller = new AbortController();
setTimeout(() => {
rates.emit("kurs", "USD", 11903.15);
rates.emit("kurs", "EUR", 13372.4);
controller.abort();
}, 5);
const got = [];
try {
const events = on(rates, "kurs", { signal: controller.signal });
for await (const [code, rate] of events) {
got.push(`${code}=${rate}`);
}
} catch (error) {
console.log(got);
console.log(error.name);
}Konsolda:
[ 'USD=11903.15', 'EUR=13372.4' ]
AbortErrorIkkala hodisa ham olindi, keyin signal bekor qilindi va sikl AbortError bilan tugadi. Kanon tajribasida ham xuddi shu: [ 1, 2 ] → AbortError (t27-events.txt).
Uch qoida:
- Sikl o'zi tugamaydi. Emitter "boshqa hodisa bo'lmaydi" demaydi. To'xtatish yo'llari:
break,signal,"error"hodisasi (sikl xato tashlaydi) yoki Node 20+ dagiclosesozlamasi ({ close: ["end"] }— shu hodisada sikl tinch tugaydi). - Navbat cheklanmagan. Hodisalar o'qilganidan tezroq kelsa, navbat o'sadi. Bu keyingi darslardagi backpressure muammosining kichik ko'rinishi (Backpressure).
- Har hodisa — massiv.
emit("kurs", "USD", 11903.15)→["USD", 11903.15].
readline dagi for await (const qator of rl) aynan shu g'oya ustida qurilgan: ichida "line" hodisalari, tashqarida — navbatli async iterator. Shuning uchun u quvurdagi qatorlarni yo'qotmadi.
4. async tinglovchi va captureRejections
4.1 Muammo: xato "error" ga bormaydi
Tinglovchi async funksiya bo'lsa, u Promise qaytaradi. emit esa sinxron va qaytgan qiymatga qaramaydi. Ichidagi xato nima bo'ladi?
import { EventEmitter } from "node:events";
const e = new EventEmitter();
e.on("kurs", async (code) => {
throw new Error(`${code} uchun kurs yo'q`);
});
e.on("error", (err) => console.log("error:", err.message));
e.emit("kurs", "XYZ");
console.log("emit qaytdi");Konsolda:
emit qaytdi
Error: XYZ uchun kurs yo'q"error" tinglovchisi bor, lekin u chaqirilmadi. Rad etilgan Promise'ni hech kim kutmadi — bu ushlanmagan rad etilish (unhandled rejection), Node esa uni ushlanmagan xato kabi to'xtatadi (exit 1) (Ushlanmagan xatolar). Oddiy (sinxron) tinglovchidagi throw dan ham yomonroq: u hech bo'lmasa emit ni chaqirgan joyga uchardi.
4.2 Yechim: captureRejections
import { EventEmitter } from "node:events";
const e = new EventEmitter({ captureRejections: true });
e.on("kurs", async (code) => {
throw new Error(`${code} uchun kurs yo'q`);
});
e.on("error", (err) => console.log("error:", err.message));
e.emit("kurs", "XYZ");
console.log("emit qaytdi");Konsolda:
emit qaytdi
error: XYZ uchun kurs yo'qcaptureRejections: true bo'lsa, emitter tinglovchi qaytargan Promise'ni kuzatadi va rad etilsa, xatoni "error" hodisasiga yuboradi. E'tibor bering: xato keyinroq keldi — "emit qaytdi" dan keyin, chunki Promise asinxron rad etildi. Kanon tajribasi: "captureRejections → error hodisasi: async tinglovchida xato".
Tartibni qadamma-qadam ko'ramiz:
| Qadam | Nima bo'ladi | Konsolda |
|---|---|---|
| 1 | emit tinglovchini sinxron chaqiradi; throw uning Promise'ini rad etadi |
— |
| 2 | emit qaytadi, keyingi qator ishlaydi |
emit qaytdi |
| 3 | sinxron kod tugagach, emitter Promise rad etilganini ko'radi | — |
| 4 | emitter xatoni "error" ga yuboradi |
error: XYZ uchun kurs yo'q |
Quyidagi diagrammada rad etilgan Promise'ning ikki yo'li bor: sozlama bilan va sozlamasiz. Qaysi yo'l jarayonni yiqitishiga qarang:
flowchart LR
E["emit('kurs')"] --> L["async tinglovchi"]
L --> P["rad etilgan Promise"]
P -->|"captureRejections: true"| R["'error' hodisasi<br/>tinglovchi ushlaydi"]
P -->|"sukut"| U["ushlanmagan rad etilish<br/>jarayon yiqiladi"]Sozlamasiz yo'lda "error" tinglovchisi umuman ishtirok etmaydi — Promise to'g'ridan-to'g'ri ushlanmagan rad etilishga aylanadi.
Hamma emitterlar uchun birdaniga: EventEmitter.captureRejections = true. Lekin xavfsizroq yo'l — async tinglovchi ichida try/catch qilish. Shunda xato qayerda ushlanishi ko'rinib turadi.
4.3 errorMonitor: xatoni kuzatish, lekin ushlamaslik
Ba'zan xatolarni faqat log qilish kerak, xulqni o'zgartirmasdan. events.errorMonitor maxsus kalit — u "error" dan oldin chaqiriladi, lekin xatoni "ushlangan" hisoblamaydi:
import { EventEmitter, errorMonitor } from "node:events";
const e = new EventEmitter();
e.on(errorMonitor, (err) => console.log("monitor:", err.message));
e.on("error", (err) => console.log("error:", err.message));
e.emit("error", new Error("tarmoq uzildi"));Konsolda:
monitor: tarmoq uzildi
error: tarmoq uzildiFaqat errorMonitor bo'lib, "error" tinglovchisi bo'lmasa — jarayon baribir yiqiladi. Monitoring kutubxonalari (Sentry kabi) shu kalitdan foydalanadi: xatoni yozib oladi, lekin dasturning xulqini o'zgartirmaydi.
5. Tinglovchi sizishi (leak)
5.1 Ogohlantirish
Bitta hodisaga 10 tadan ko'p tinglovchi qo'shilsa, Node ogohlantiradi. Kanon tajribasi (t27-events.txt):
(node:7132) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 x listeners added to [EventEmitter]. MaxListeners is 10. Use emitter.setMaxListeners() to increase limitTarjimasi: "EventEmitter'da xotira sizishi bo'lishi mumkin. x hodisasiga 11 ta tinglovchi qo'shildi. Chegara — 10. Oshirish uchun emitter.setMaxListeners()". Bu xato emas — dastur ishlashda davom etadi. Lekin bu ogohlantirish deyarli har doim haqiqiy muammoni ko'rsatadi.
5.2 Odatiy sabab
Har chaqiruvda tinglovchi qo'shib, olib tashlamaslik:
import { EventEmitter } from "node:events";
process.on("warning", (w) => console.log(w.name, w.count));
const app = new EventEmitter();
async function fetchRate(code) {
app.on("toxta", () => console.log(`${code} bekor qilindi`)); // ❌
return `${code}: ok`;
}
for (let i = 1; i <= 11; i++) await fetchRate(`K${i}`);
await new Promise((r) => setImmediate(r));
console.log("tinglovchilar:", app.listenerCount("toxta"));Konsolda:
MaxListenersExceededWarning 11
tinglovchilar: 11Har fetchRate chaqiruvi "to'xtash" tinglovchisini qo'shdi va hech qachon olib tashlamadi. 11 ta so'rov — 11 ta tinglovchi; server kun bo'yi ishlasa — millionlab. Har tinglovchi o'zi ushlab turgan o'zgaruvchilarni (code) ham xotirada saqlaydi. Bu klassik xotira sizishi (memory leak). Uni Memory leak'ni topish darsida heap snapshot bilan ko'ramiz.
process.on("warning") — ogohlantirishlarni dasturda ushlash. Ogohlantirish process emitterining "warning" hodisasi sifatida keladi.
5.3 To'g'ri tuzatish
setMaxListeners(100) — tuzatish emas, ogohlantirishni o'chirish. Haqiqiy tuzatish — tinglovchini ishi tugagach olib tashlash:
import { EventEmitter } from "node:events";
const app = new EventEmitter();
async function fetchRate(code) {
const onStop = () => console.log(`${code} bekor qilindi`);
app.on("toxta", onStop);
try {
return `${code}: ok`;
} finally {
app.off("toxta", onStop); // ✅ har holda olib tashlanadi
}
}
for (let i = 1; i <= 11; i++) await fetchRate(`K${i}`);
console.log(app.listenerCount("toxta")); // 0finally xato bo'lsa ham ishlaydi. Zamonaviy kodda bunday "bekor qilish" odatda AbortSignal orqali beriladi — u tinglovchini o'zi boshqaradi.
setMaxListeners qachon o'rinli? Tinglovchilar soni haqiqatan katta va cheklangan bo'lsa: masalan, bitta signalni 50 ta parallel so'rov tinglaydi va hammasi tugagach o'chadi. Shunda chegarani oshirish to'g'ri. getEventListeners(emitter, nom) — tinglovchilar ro'yxati, tekshirish uchun.
Tekshirib ko'ring: Ogohlantirishda
11 x listeners added. Keyingi kun xuddi shu xabar11 SIGINT listeners added to [process]bo'lib keldi. Qayerdan qidirasiz?
Javob
process.on("SIGINT", …) yoki process.once(…) chaqiruvi takrorlanadigan funksiya ichida turibdi — masalan har so'rovda Ctrl+C ishlovchisi qo'shilyapti. Uni bir marta, dastur boshida qo'shish yoki ish tugagach off qilish kerak. node --trace-warnings bilan ishga tushirsangiz, ogohlantirish qaysi qatordan kelganini ko'rsatadi.
6. EventTarget va CustomEvent
Brauzerda hodisalar EventTarget orqali ishlaydi (addEventListener, addEventListener asoslari). Node 15 dan beri EventTarget va Event Node'da ham global, CustomEvent esa Node 19 dan. Ular brauzerdagi API bilan bir xil — shuning uchun bu blok saytda ham ishlaydi:
const shop = new EventTarget();
shop.addEventListener(
"kurs",
(event) => console.log(event.type, event.detail.USD),
{ once: true },
);
const first = { detail: { USD: 11903.15 } };
shop.dispatchEvent(new CustomEvent("kurs", first));
shop.dispatchEvent(new CustomEvent("kurs", { detail: { USD: 0 } }));
console.log("tugadi");Konsolda:
kurs 11903.15
tugadiIkkinchi dispatchEvent jim o'tdi — { once: true } tinglovchini olib tashlagan edi (Listener opsiyalari). Ma'lumot detail ichida keladi.
EventEmitter |
EventTarget |
|
|---|---|---|
| Qayerdan | node:events |
global (brauzer va Node) |
| Tinglash | on(nom, fn) |
addEventListener(nom, fn, opsiyalar) |
| Chiqarish | emit(nom, ...args) |
dispatchEvent(new Event(nom)) |
| Ma'lumot | istalgan argumentlar | Event obyekti (detail — CustomEvent da) |
"error" |
maxsus — tinglovchisiz throw |
maxsus emas |
| Bekor qilish | off yoki events.on/once ning signal i |
{ signal } opsiyasi |
| Bir funksiya ikki marta | ikki marta chaqiriladi | bir marta |
Qachon qaysi? Node'ning o'z API'lari (stream, soket, server) — EventEmitter. Brauzer va Node'da bir xil ishlashi kerak bo'lgan kutubxona yozsangiz — EventTarget. Node'ning o'zida ham EventTarget bor joylar: AbortSignal, MessagePort (worker'lar orasida). Shuning uchun once(signal, "abort") ham ishlaydi — events.once ikkala turni ham qo'llaydi.
7. CLI qadami: holat yo'q — kanon tajribasi
EventEmitter asoslari darsida aytilgandek, valyuta da hodisa manbai yo'q va bu dars ham loyihaga kod qo'shmaydi (kanon qarori). Lekin uning saboqlari valyuta ning tuzilishida ko'rinadi:
for awaitemitter o'rniga.interaktiv.tsreadlinenirl.on("line")bilan emas,for awaitbilan o'qiydi —events.ong'oyasi. Natijada xatoawaitga tushadi vatry/catchbilan ushlanadi.once— testlarda.interaktiv.test.tsdagiawait new Promise((r) => bola.on("exit", r))niconst [kod] = await once(bola, "exit")deb yozish mumkin — qisqaroq va bola"error"chiqarsa, test osilib qolmaydi, rad etiladi.- Signal bilan bekor qilish. timers darsidagi
AbortSignal.any([signal, vaqt])—EventTargetasosidagi signal; uning"abort"hodisasinifetchvatimers/promiseso'zlari tinglaydi.
Kanon tajribasi (t27-events.txt) bu darsga tegishli qatorlari: await once(e, 'tayyor') → [ 42 ]; 11 tinglovchi → MaxListenersExceededWarning; captureRejections: true → xato "error" ga; on(…, { signal }) → [ 1, 2 ] keyin AbortError. Bu darsdagi qolgan misollar — biz ishga tushirgan. Bu darsda commit yo'q.
8. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
MaxListenersExceededWarning |
Tinglovchi qo'shiladi, olinmaydi | off (finally da) yoki once |
setMaxListeners(Infinity) bilan "tuzatilgan" |
Ogohlantirish o'chirildi, leak qoldi | Sababini toping |
async tinglovchi xatosi jarayonni yiqitdi |
Rad etilgan Promise kutilmadi | try/catch ichida yoki captureRejections |
for await (… of on(…)) tugamaydi |
Iterator o'zi tugamaydi | break, signal, close sozlamasi |
await once(…, { signal: AbortSignal.timeout }) — exit 13 |
Timeout taymeri unref |
Jarayonda boshqa ish bo'lsin; testda — ref qilingan taymer |
once(socket, "data") — ma'lumot yo'qoldi |
Ko'p hodisaga bir martalik kutish | on yoki stream iteratori |
9. Mashqlar
Mashqlar kurs/mashqlar/24/28-eventemitter-chuqur/ da.
1-mashq (oson): to'g'ri vositani tanlang
- Server
"listening"bo'lgunchaawait— - Kelayotgan kurslarni
for awaitbilan o'qish — asynctinglovchi xatosini"error"ga yo'naltirish —captureRejections:- Xatoni faqat log qilish, ushlamasdan —
events. - Brauzer va Node'da bir xil hodisa obyekti —
Yechim
- once — bir martalik signal.
- on — async iterator, navbatli.
- true — emitter yaratishda.
- errorMonitor —
"error"dan oldin chaqiriladi. - CustomEvent — ma'lumot
detailda.
2-mashq (o'rta): birinchisi qaysi?
firstOf(emitter, names) funksiyasini yozing: ro'yxatdagi hodisalardan qaysi biri birinchi yuz bersa, uning nomi va argumentlarini qaytarsin ({ name, args }). Qolgan kutishlar bekor qilinsin — tinglovchilar qolmasin. once ga signal berish va Promise.race ishlating. Sinang: 20 ms da "xato", 10 ms da "tayyor".
Yechim
import { EventEmitter, once } from "node:events";
async function firstOf(emitter, names) {
const controller = new AbortController();
const waits = names.map((name) =>
once(emitter, name, { signal: controller.signal })
.then((args) => ({ name, args })),
);
try {
return await Promise.race(waits);
} finally {
controller.abort(); // qolgan kutishlar tinglovchilari o'chadi
await Promise.allSettled(waits);
}
}
const order = new EventEmitter();
setTimeout(() => order.emit("xato", "kuyib ketdi"), 20);
setTimeout(() => order.emit("tayyor", "osh"), 10);
console.log(await firstOf(order, ["tayyor", "xato"]));
console.log("qolgan:", order.listenerCount("xato"));Konsolda:
{ name: 'tayyor', args: [ 'osh' ] }
qolgan: 0"tayyor" 10 ms da keldi. finally dagi abort() "xato" kutishini bekor qildi va uning tinglovchisini olib tashladi — qolgan: 0. allSettled bekor qilingan Promise'ning rad etilishini "ushlab" oladi, aks holda u ushlanmagan rad etilish bo'lardi.
3-mashq (qiyin): kurslar oqimini for await bilan yig'ish
Kurs manbai (EventEmitter) "kurs" hodisalarini (code, rate) va oxirida "tugadi" ni chiqaradi. collect(source) funksiyasini yozing: events.on bilan hamma kurslarni o'qisin, "tugadi" kelganda tinch to'xtasin va har valyutaning oxirgi kursini Map da qaytarsin. Maslahat: on ning close: ["tugadi"] sozlamasi. Manba: USD 11 900, EUR 13 370, USD 11 903.15, keyin tugadi.
Yechim
import { EventEmitter, on } from "node:events";
async function collect(source) {
const last = new Map();
const events = on(source, "kurs", { close: ["tugadi"] });
for await (const [code, rate] of events) last.set(code, rate);
return last;
}
const source = new EventEmitter();
setTimeout(() => {
source.emit("kurs", "USD", 11900);
source.emit("kurs", "EUR", 13370);
source.emit("kurs", "USD", 11903.15);
source.emit("tugadi");
}, 5);
const rates = await collect(source);
console.log(rates);
console.log("tinglovchilar:", source.listenerCount("kurs"));Konsolda:
Map(2) { 'USD' => 11903.15, 'EUR' => 13370 }
tinglovchilar: 0close: ["tugadi"] — shu hodisa kelganda iterator xatosiz tugaydi va for await dan chiqiladi. Sikl tugagach on tinglovchilarini o'zi olib tashladi. Bu readline dagi "close" ga o'xshaydi: oqim tugadi — sikl tugadi.
4-mashq: CLI qadami — testni once bilan qayta yozing
O'z valyuta ingizda test/interaktiv.test.ts dagi await new Promise((r) => bola.on("exit", r)) ni events.once bilan yozing. npm test 33/33 o'tishini tekshiring. Kanonda bu o'zgarish yo'q — commit o'zingizning xohishingiz bilan.
Yechim
import { once } from "node:events";
// …
const [kod] = await once(bola, "exit");
assert.equal(kod, 0);Farqi: bola ishga tushmasa ("error", masalan ENOENT), eski kod abadiy kutardi — "exit" hech qachon kelmaydi. once esa "error" da rad etadi va test aniq xato bilan yiqiladi.
10. Real ishda
- Testlar —
await once(server, "listening"),await once(child, "exit")— Node testlarining eng ko'p uchraydigan qatorlari. - Graceful shutdown —
await once(process, "SIGTERM"), keyin serverni yopish. Deploy qismlarida ko'ramiz. - Leak qidirish —
MaxListenersExceededWarningproduction logida — birinchi navbatda tekshiriladigan narsa;--trace-warningsmanbasini ko'rsatadi. - Universal kutubxonalar — brauzer va Node'da ishlashi kerak bo'lgan SDK'lar
EventTargetni tanlaydi. - Intervyuda: "
asynctinglovchidagi xato qayerga ketadi?" —"error"ga emas, ushlanmagan rad etilishga (captureRejectionsbo'lmasa).
Xulosa
await once(e, "nom")— bir martalik hodisa, natija — argumentlar massivi;"error"— rad etish.for await (… of on(e, "nom"))— navbatli hodisalar oqimi;signal,breakyokiclosebilan to'xtatiladi.AbortSignal.timeouttaymeriunref: boshqa ish bo'lmasa,awaitosilib, exit 13 (biz o'lchadik).asynctinglovchi xatosi"error"ga bormaydi —try/catchyokicaptureRejections: true.MaxListenersExceededWarning— odatda leak: tinglovchinifinallydaoffqiling, chegarani ko'r-ko'rona oshirmang.
Keyingi dars: Buffer va binar ma'lumot — soket va fayldan keladigan bo'laklar aslida nima: baytlar, kodlashlar va o'zbekcha ʻ belgisi necha bayt.
Manbalar
- Node.js hujjati — Events:
events.once,events.on(closesozlamasi),captureRejections,errorMonitor,setMaxListeners,getEventListeners,EventTargetvaCustomEvent: https://nodejs.org/docs/latest-v24.x/api/events.html - Node.js hujjati — Process:
"warning"hodisasi,--trace-warnings: https://nodejs.org/docs/latest-v24.x/api/process.html#event-warning - Kanon:
natijalar/tajriba/t27-events.txt(Node 24.21.0, Windows 11, 2026-10-23).AbortSignal.timeoutbilan exit 13 topilmasi va qolgan misollar — biz ishga tushirdik (Node 24.21.0, Windows 11, 2026-10-23).
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!