EventEmitter (node:events) în Node.js

Evenimente, stream-uri și date binare Node.js 0.1; events.once din 11.13, events.on din 13.6

Clasa pentru evenimente din Node.js: înregistrezi ascultători cu on și îi declanșezi cu emit. Baza pentru stream-uri și servere.

Sintaxă
import { EventEmitter, once } from "node:events";
const e = new EventEmitter();
e.on(nume, ascultator);  e.once(nume, ascultator);  e.emit(nume, ...argumente);  e.off(nume, ascultator);
await once(e, nume);

Ce face EventEmitter (node:events)

Un EventEmitter ține o listă de funcții (ascultători) pentru fiecare nume de eveniment. on(nume, fn) adaugă un ascultător, once unul care rulează o singură dată, off (sau removeListener) îl scoate, iar emit(nume, ...argumente) le apelează pe toate, sincron, în ordinea înregistrării. Stream-urile, serverul HTTP și procesele copil sunt toate EventEmitter.

Evenimentul "error" este special: dacă îl emiți și nu are niciun ascultător, eroarea este aruncată și oprește procesul. De aceea orice obiect care poate emite erori trebuie să aibă .on("error", ...).

Funcțiile events.once(emitter, nume) și events.on(emitter, nume) leagă evenimentele de async/await: prima întoarce o promisiune pentru următorul eveniment, a doua un iterator asincron pentru toate evenimentele. Peste 10 ascultători pe același eveniment, Node.js afișează un avertisment (posibilă scurgere de memorie).

Parametri

Parametru Descriere
on(nume, fn) / addListener Adaugă un ascultător.
once(nume, fn) Ascultător care se scoate singur după primul apel.
emit(nume, ...arg) Apelează ascultătorii; întoarce true dacă a existat cel puțin unul.
off(nume, fn) / removeListener Scoate un ascultător (aceeași funcție dată la on).
listenerCount(nume) Numărul de ascultători.

Ce returnează

on, once, off întorc emițătorul (se pot înlănțui); emit întoarce true sau false.

Exemple

Exemplul 1: O clasă care emite evenimente

import { EventEmitter } from "node:events";

class Magazin extends EventEmitter {
  #stoc = new Map([["mere", 2]]);
  vinde(produs) {
    const ramas = (this.#stoc.get(produs) ?? 0) - 1;
    if (ramas < 0) return this.emit("error", new Error(`Nu mai avem ${produs}`));
    this.#stoc.set(produs, ramas);
    this.emit("vanzare", produs, ramas);
    if (ramas === 0) this.emit("stoc-epuizat", produs);
  }
}

const m = new Magazin();
m.on("vanzare", (produs, ramas) => console.log(`Vândut: ${produs}, rămân ${ramas}`));
m.once("stoc-epuizat", (produs) => console.log(`Comandă nouă de ${produs}!`));
m.on("error", (e) => console.log("Eroare:", e.message));

m.vinde("mere");
m.vinde("mere");
m.vinde("mere");

Rezultat

Vândut: mere, rămân 1
Vândut: mere, rămân 0
Comandă nouă de mere!
Eroare: Nu mai avem mere

Exemplul 2: off și once

import { EventEmitter } from "node:events";

const sonerie = new EventEmitter();
const ana = () => console.log("Ana deschide ușa");
sonerie.on("suna", ana);
sonerie.once("suna", () => console.log("Câinele latră (doar prima dată)"));

sonerie.emit("suna");
sonerie.emit("suna");
sonerie.off("suna", ana);
console.log("Ascultători rămași:", sonerie.listenerCount("suna"));
console.log("emit fără ascultători:", sonerie.emit("suna"));

Rezultat

Ana deschide ușa
Câinele latră (doar prima dată)
Ana deschide ușa
Ascultători rămași: 0
emit fără ascultători: false

Exemplul 3: Așteptare cu await: events.once și events.on

import { EventEmitter, once, on } from "node:events";

const curier = new EventEmitter();
setTimeout(() => curier.emit("livrat", "Iași", "colet 77"), 10);

const [oras, colet] = await once(curier, "livrat");
console.log(`${colet} livrat în ${oras}`);

const ac = new AbortController();
setTimeout(() => {
  for (const nota of [9, 10, 7]) curier.emit("nota", nota);
  ac.abort();
}, 10);
try {
  for await (const [nota] of on(curier, "nota", { signal: ac.signal })) console.log("Notă:", nota);
} catch (e) {
  console.log(e.name);
}

Rezultat

colet 77 livrat în Iași
Notă: 9
Notă: 10
Notă: 7
AbortError

Greșeli frecvente

  • Nu pui ascultător pentru "error": un singur emit("error") oprește tot programul.
  • Scoți ascultătorul cu o funcție nouă: off("x", () => ...) nu scoate nimic, pentru că este altă funcție. Păstrează referința dată la on.
  • Te aștepți ca emit să fie asincron: ascultătorii rulează imediat, înainte ca emit să se întoarcă.

Comenzi înrudite

  • stream.Readable: Stream-ul din care citești date bucată cu bucată: fișiere, cereri HTTP, stdin. Readable.from îl creează din orice iterabil.
  • stream.pipeline: Leagă stream-uri în lanț (citire → transformare → scriere), cu tratarea erorilor și închiderea tuturor la final.
  • http.createServer: Pornește un server web cu modulul de bază node:http: primești cererea (req) și trimiți răspunsul (res).
  • timers/promises (setTimeout cu await): Variantele cu promisiuni ale temporizatoarelor: await setTimeout(1000) pentru o pauză, setInterval ca iterator asincron.