IlmHamroh
JavaScript Full-stack/24-qism. Node.js28/55-dars16 daqiqa
Mundarija (26)

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. async tinglovchidagi xato "error" ga o'tmaydi — jarayon yiqiladi (captureRejections: true buni tuzatadi). Bitta hodisaga 11-tinglovchi qo'shilsa — MaxListenersExceededWarning: odatda bu tinglovchi sizishi (leak) belgisi.

Bu darsda

  • events.once bilan hodisani await qilasiz va timeout qo'yasiz.
  • events.on bilan hodisalarni for await siklida o'qiysiz.
  • async tinglovchidagi xato qayerga ketishini va captureRejections ni bilasiz.
  • Leak ogohlantirishini o'qib, sababini topasiz va tuzatasiz.
  • Node'dagi EventTarget va CustomEvent ni EventEmitter bilan 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":

js
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:

js
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 2

Massiv 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:

js
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:

js
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:

text
ushlandi: CBU javob bermadi

Endi 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):

js
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:

text
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:

js
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:

text
[ 'USD=11903.15', 'EUR=13372.4' ]
AbortError

Ikkala 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+ dagi close sozlamasi ({ 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?

js
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:

text
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

js
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:

text
emit qaytdi
error: XYZ uchun kurs yo'q

captureRejections: 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:

js
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:

text
monitor: tarmoq uzildi
error: tarmoq uzildi

Faqat 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):

text
(node:7132) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 x listeners added to [EventEmitter]. MaxListeners is 10. Use emitter.setMaxListeners() to increase limit

Tarjimasi: "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:

js
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:

text
MaxListenersExceededWarning 11
tinglovchilar: 11

Har 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:

js
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")); // 0

finally 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 xabar 11 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:

js
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:

text
kurs 11903.15
tugadi

Ikkinchi 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 await emitter o'rniga. interaktiv.ts readline ni rl.on("line") bilan emas, for await bilan o'qiydi — events.on g'oyasi. Natijada xato await ga tushadi va try/catch bilan ushlanadi.
  • once — testlarda. interaktiv.test.ts dagi await new Promise((r) => bola.on("exit", r)) ni const [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]) — EventTarget asosidagi signal; uning "abort" hodisasini fetch va timers/promises o'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'lguncha await —
  • Kelayotgan kurslarni for await bilan o'qish —
  • async tinglovchi 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 detail da.

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
js
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:

text
{ 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
js
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:

text
Map(2) { 'USD' => 11903.15, 'EUR' => 13370 }
tinglovchilar: 0

close: ["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
ts
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 — MaxListenersExceededWarning production logida — birinchi navbatda tekshiriladigan narsa; --trace-warnings manbasini ko'rsatadi.
  • Universal kutubxonalar — brauzer va Node'da ishlashi kerak bo'lgan SDK'lar EventTarget ni tanlaydi.
  • Intervyuda: "async tinglovchidagi xato qayerga ketadi?" — "error" ga emas, ushlanmagan rad etilishga (captureRejections bo'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, break yoki close bilan to'xtatiladi.
  • AbortSignal.timeout taymeri unref: boshqa ish bo'lmasa, await osilib, exit 13 (biz o'lchadik).
  • async tinglovchi xatosi "error" ga bormaydi — try/catch yoki captureRejections: true.
  • MaxListenersExceededWarning — odatda leak: tinglovchini finally da off qiling, 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

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Node.js EventEmitter chuqur: events.once, events.on, leak ogohlantirishi va EventTarget — IlmHamroh