stream.Readable în Node.js

Evenimente, stream-uri și date binare Node.js 0.9.4; Readable.from din 10.17 / 12.3

Stream-ul din care citești date bucată cu bucată: fișiere, cereri HTTP, stdin. Readable.from îl creează din orice iterabil.

Sintaxă
import { Readable } from "node:stream";
const r = Readable.from(iterabil);
for await (const bucata of r) { ... }
r.on("data", ...); r.on("end", ...);

Ce face stream.Readable

Un Readable este o sursă de date care vin în bucăți: fs.createReadStream, corpul unei cereri HTTP, process.stdin, ieșirea unui proces copil. Cel mai simplu le citești cu for await (const bucata of stream); bucla se termină singură la finalul datelor.

Varianta mai veche, cu evenimente: "data" pentru fiecare bucată, "end" la final, "error" la eroare. Bucățile sunt Buffer, dacă nu setezi o codificare (setEncoding("utf8") sau opțiunea encoding).

Readable.from(iterabil) face un stream dintr-un tablou, un generator sau un generator asincron. În modul obiect (implicit la from), bucățile pot fi orice valori, nu doar text. Stream-urile au și metode ca la tablouri: map, filter, take, toArray; acestea sunt încă experimentale.

Parametri

Parametru Descriere
Readable.from(iterabil, [opțiuni]) Creează un stream din iterabil.
for await ... of Citește bucățile pe rând.
evenimente data / end / error Bucată nouă / final / eroare.
setEncoding(cod) Bucățile vin ca text în codificarea dată.

Exemple

Exemplul 1: Citirea unui fișier pe bucăți

highWaterMark stabilește mărimea bucăților; aici e mică, ca să vedem mai multe.

import { createReadStream, writeFileSync } from "node:fs";

writeFileSync("comenzi.txt", "x".repeat(100));

let bucati = 0, total = 0;
for await (const bucata of createReadStream("comenzi.txt", { highWaterMark: 32 })) {
  bucati++;
  total += bucata.length;
}
console.log(`${bucati} bucăți, ${total} octeți`);

Rezultat

4 bucăți, 100 octeți

Exemplul 2: Readable.from cu un generator

import { Readable } from "node:stream";

function* comenzi() {
  yield { id: 1, client: "Ana", total: 120 };
  yield { id: 2, client: "Ion", total: 45 };
  yield { id: 3, client: "Maria", total: 300 };
}

const stream = Readable.from(comenzi());
stream.on("data", (c) => console.log(`#${c.id} ${c.client}: ${c.total} lei`));
stream.on("end", () => console.log("Gata, toate comenzile citite."));

Rezultat

#1 Ana: 120 lei
#2 Ion: 45 lei
#3 Maria: 300 lei
Gata, toate comenzile citite.

Exemplul 3: Citire de pe stdin

process.stdin este tot un Readable. Programul citește tot ce primește, apoi numără cuvintele.

let text = "";
process.stdin.setEncoding("utf8");
for await (const bucata of process.stdin) text += bucata;

const cuvinte = text.split(/\s+/).filter(Boolean);
console.log(`${cuvinte.length} cuvinte`);

Ce scrie utilizatorul (intrare)

Ana are mere și pere
Ion are prune

Rezultat

8 cuvinte

Greșeli frecvente

  • Concatenezi bucăți Buffer ca text cu += fără codificare: un caracter ca „ș” (2 octeți) poate fi tăiat între două bucăți și apare stricat. Setează encoding: "utf8" sau adună Buffer-ele și transformă-le la final.
  • Atașezi ascultătorul "data" și apoi folosești și for await pe același stream: datele se împart între cele două. Alege un singur mod de citire.

Comenzi înrudite

  • stream.pipeline: Leagă stream-uri în lanț (citire → transformare → scriere), cu tratarea erorilor și închiderea tuturor la final.
  • stream.Transform: Stream care primește date, le modifică și le dă mai departe: comprimare, criptare, împărțire pe rânduri, conversii.
  • readline (citire pe rânduri): Citește un fișier sau tastatura rând cu rând și pune întrebări utilizatorului în terminal (question).
  • Buffer: Șir de octeți din Node.js: date binare din fișiere, rețea sau criptografie, cu conversii în text, hex și base64.
  • EventEmitter (node:events): Clasa pentru evenimente din Node.js: înregistrezi ascultători cu on și îi declanșezi cu emit. Baza pentru stream-uri și servere.