„Node.js“ garsėja dėl savo sugebėjimo efektyviai valdyti I/O operacijas, o šios galimybės esmė yra sąvoka srautai. Srautai leidžia jums apdoroti duomenis po gabalu, o ne įkelti viską į atmintį vienu metu-puikiai tinka tvarkyti didelius failus, tinklo užklausas ar realiojo laiko duomenis. Kai suporuojate srautus su stipriu „TypeScript“ spausdinimu, gausite galingą kombinaciją: našumas atitinka saugumą.
Šiame vadove mes pasineriame giliai į „Node.js“ srautus, tyrinėsime jų tipus ir apžiūrėsime praktinius pavyzdžius naudodami „TypeScript“. Nesvarbu, ar esate „Node.js Newbie“, ar „TypeScript“ entuziastas, norintis pakilti, šis įrašas jus apėmė.
Kodėl srautai yra svarbūs?
Paveikslėlis: Jums pavesta apdoroti 50 GB žurnalo failą. Įkeliant jį į atmintį, jūsų serverio ištekliai išnaudotų, todėl susiduria su avarijomis ar vangiais našumais. Srautai tai išsprendžia leisdami jums tvarkyti duomenis, kai jie teka, pavyzdžiui, gurkšnoti nuo šiaudų, užuot gurkšnojant galono indą.
Dėl šios efektyvumo srautai yra kertinis „Node.js“ akmuo, maitinantis viską, pradedant failų operacijomis ir baigiant HTTP serveriais. „TypeScript“ sustiprina tai pridedant tipo apibrėžimus, sugaudamas klaidas kompiliavimo metu ir pagerindamas kodo skaitomumą. Pasinerkime į pagrindus ir pažiūrėkime, kaip ši sinergija veikia praktikoje.
Keturių srautų tipai
„Node.js“ siūlo keturis pagrindinius srauto tipus, kurių kiekvienas turi konkretų tikslą:
- Skaitomi srautai: Duomenų šaltiniai, kuriuos galite perskaityti iš (pvz., Failų, HTTP atsakymų).
- Rašymo srautai: Tikslas, į kurį galite rašyti (pvz., Failai, HTTP užklausos).
- Dvipusiai srautai: Ir skaitomi, ir rašomi (pvz., TCP lizdai).
- Transformuoti srautus: Specialus dvipusis srautas, modifikuojantis duomenis, kai praeina per (pvz., Suspaudimas).
„TypeScript“ tai pagerina, leisdama mums apibrėžti per juos tekančių duomenų sąsajas. Suskirstykime juos su pavyzdžiais.
Savo „TypeScript“ aplinkos nustatymas
Prieš pasinerdami į kodą, įsitikinkite, kad įdiegėte „Node.js“ ir „TypeScript“.
Sukurkite naują projektą:
mkdir node-streams-typescript
cd node-streams-typescript
npm init -y
npm install typescript @types/node --save-dev
npx tsc --init
Atnaujinkite savo „TSconfig.json“, kad įtrauktumėte:
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"strict": true,
"outDir": "./dist"
},
"include": ("src/**/*")
}
Sukurkite SRC aplanką ir pradėkime kodavimą!
1 pavyzdys: Failo skaitymas su skaitomu srautu
Perskaitykime „Chunk“ teksto failą. Pirmiausia sukurkite failą pavadinimu Data.txt savo projekto pagrindiniame kataloge su tam tikru pavyzdžiu (pvz., „Sveiki, srautai!“).
Dabar SRC/ReadStream.ts:
import { createReadStream } from 'fs';
import { Readable } from 'stream';
const readStream: Readable = createReadStream('data.txt', { encoding: 'utf8' });
readStream
.on('data', (chunk: string) => {
console.log('Chunk received:', chunk);
})
.on('end', () => {
console.log('Finished reading the file.');
})
.on('error', (err: Error) => {
console.error('Error:', err.message);
});
Paleiskite tai:
npx tsc && node dist/readStream.js
Čia „TypeScript“ užtikrina, kad riekė priliptų prie mūsų šoninės sąsajos, o klaidų įvykio tvarkytojas tikisi klaidų tipo. Šis srautas skaito „Data.txt“ gabalėliuose (numatytasis 64KB failams) ir juos registruoja.
2 pavyzdys: Duomenų rašymas su rašymo srautu
Dabar parašykime duomenis į naują failą. SRC/WriteStream.ts:
import { createWriteStream } from 'fs';
import { Writable } from 'stream';
const writeStream: Writable = createWriteStream('output.txt', { encoding: 'utf8' });
const data: string() = ('Line 1\n', 'Line 2\n', 'Line 3\n');
data.forEach((line: string) => {
writeStream.write(line);
});
writeStream.end(() => {
console.log('Finished writing to output.txt');
});
writeStream.on('error', (err: Error) => {
console.error('Error:', err.message);
});
Sudarykite ir paleiskite:
npx tsc && node dist/writeStream.js
Tai sukuria išvestį.txt trimis eilutėmis. „TypeScript“ užtikrina, kad eilutė yra eilutė, ir suteikia srauto metodams automatinį užbaigimą.
3 pavyzdys: vamzdynai su transformacijos srautu
Vamzdynai yra ten, kur šviečia srautai, jungiantys skaitomą srautą prie rašymo srauto. Pridėkime posūkį su a Transformuoti Srautuokite į mūsų tekstą.
SRC/transformstream.ts:
import { createReadStream, createWriteStream } from 'fs';
import { Transform, TransformCallback } from 'stream';
class UppercaseTransform extends Transform {
_transform(chunk: Buffer, encoding: string, callback: TransformCallback): void {
const upperChunk = chunk.toString().toUpperCase();
this.push(upperChunk);
callback();
}
}
const readStream = createReadStream('data.txt', { encoding: 'utf8' });
const writeStream = createWriteStream('output_upper.txt');
const transformStream = new UppercaseTransform();
readStream
.pipe(transformStream)
.pipe(writeStream)
.on('finish', () => {
console.log('Transform complete! Check output_upper.txt');
})
.on('error', (err: Error) => {
console.error('Error:', err.message);
});
Paleiskite:
npx tsc && node dist/transformStream.js
Tai skaito „Data.txt“, paverčia tekstą į didžiąsias raides ir rašo jį į „Output_upper.txt“.
„TypeScript“ „TradingCallback“ tipas užtikrina, kad mūsų _Transform metodas būtų teisingai įgyvendintas.
4 pavyzdys: Failų suspaudimas su dvipusiu srautu
Spręskime sudėtingesnį scenarijų: failo suspaudimą naudojant „Zlib“ modulį, kuris suteikia dvipusį srautą. Jis pateikiamas su paketu „@Type/Node“, kurį mes įdiegėme anksčiau.
Src/compresstream.ts:
import { createReadStream, createWriteStream } from 'fs';
import { createGzip } from 'zlib';
import { pipeline } from 'stream';
const source = createReadStream('data.txt');
const destination = createWriteStream('data.txt.gz');
const gzip = createGzip();
pipeline(source, gzip, destination, (err: Error | null) => {
if (err) {
console.error('Compression failed:', err.message);
return;
}
console.log('File compressed successfully! Check data.txt.gz');
});
Paleiskite:
npx tsc && node dist/compressStream.js
Čia dujotiekis užtikrina tinkamą klaidų tvarkymą ir valymą. „GZIP“ srautas suspaudžia duomenis.txt į data.txt.gz. „TypeScript“ tipo išvados palaiko mūsų kodą švarų ir saugų.
5 pavyzdys: HTTP atsakymų srautinis perdavimas
Srautai šviečia tinklo operacijose. Imuliuokime srautinius duomenis iš HTTP serverio, naudodami „AxioOS“. Įdiekite:
npm install axios @types/axios
Src/httpstream.ts:
import axios from 'axios';
import { createWriteStream } from 'fs';
import { Writable } from 'stream';
async function streamHttpResponse(url: string, outputFile: string): Promise<void> {
const response = await axios({
method: 'get',
url,
responseType: 'stream',
});
const writeStream: Writable = createWriteStream(outputFile);
response.data.pipe(writeStream);
return new Promise((resolve, reject) => {
writeStream.on('finish', () => {
console.log(`Downloaded to ${outputFile}`);
resolve();
});
writeStream.on('error', (err: Error) => {
console.error('Download failed:', err.message);
reject(err);
});
});
}
streamHttpResponse('https://example.com', 'example.html').catch(console.error);
Paleiskite:
npx tsc && node dist/httpStream.js
Tai perduoda HTTP atsakymą (pvz., Tinklalapį) į pavyzdį.html. „TypeScript“ užtikrina, kad URL ir išvesties failo parametrai yra eilutės, o pažadų rašymas padidina aiškumą.
Mes taip pat galime naudoti „Node.js“ įmontuotą „Fetch“ API (prieinamą nuo v18 mazgo) arba bibliotekų, tokių kaip „Node-Fetch“, kuri taip pat palaiko srautinio perdavimo atsakymus, nors srautų tipai gali skirtis („Web Stream“ ir „Node.js“ srautai).
Pavyzdys:
const response = await fetch('https://example.com');
const writeStream = createWriteStream(outputFile);
response.body.pipe(writeStream);
6 pavyzdys: realiojo laiko duomenų apdorojimas naudojant pritaikytą skaitomą srautą
Sukurkime pritaikytą, skaitomą srautą, kad būtų galima modeliuoti realaus laiko duomenis, pavyzdžiui, jutiklių rodmenis. SRC/CustomReadable.ts:
import { Readable } from 'stream';
class SensorStream extends Readable {
private count: number = 0;
private max: number = 10;
constructor(options?: any) {
super(options);
}
_read(): void {
if (this.count < this.max) {
const data = `Sensor reading ${this.count}: ${Math.random() * 100}\n`;
this.push(data);
this.count++;
} else {
this.push(null);
}
}
}
const sensor = new SensorStream({ encoding: 'utf8' });
sensor
.on('data', (chunk: string) => {
console.log('Received:', chunk.trim());
})
.on('end', () => {
console.log('Sensor stream complete.');
})
.on('error', (err: Error) => {
console.error('Error:', err.message);
});
Paleiskite:
npx tsc && node dist/customReadable.js
Tai sukuria 10 atsitiktinių „jutiklių rodmenų“ ir juos transliuoja. „TypeScript“ klasės spausdinimas užtikrina, kad mūsų įgyvendinimas atitiktų skaitomą sąsają.
7 pavyzdys: Kelių transformacijos srautų sujungimas
Grandinė virsta apdorojant tekstą etapais: Didžiosios raidės, tada išankstiniu laiko žyma. Src/chainttransform.ts:
import { createReadStream, createWriteStream } from 'fs';
import { Transform, TransformCallback } from 'stream';
class UppercaseTransform extends Transform {
_transform(chunk: Buffer, encoding: string, callback: TransformCallback): void {
this.push(chunk.toString().toUpperCase());
callback();
}
}
class TimestampTransform extends Transform {
_transform(chunk: Buffer, encoding: string, callback: TransformCallback): void {
const timestamp = new Date().toISOString();
this.push(`(${timestamp}) ${chunk.toString()}`);
callback();
}
}
const readStream = createReadStream('data.txt', { encoding: 'utf8' });
const writeStream = createWriteStream('output_chain.txt');
const upper = new UppercaseTransform();
const timestamp = new TimestampTransform();
readStream
.pipe(upper)
.pipe(timestamp)
.pipe(writeStream)
.on('finish', () => {
console.log('Chained transform complete! Check output_chain.txt');
})
.on('error', (err: Error) => {
console.error('Error:', err.message);
});
Paleiskite:
npx tsc && node dist/chainTransform.js
Tai skaito „Data.txt“, viršutinę dalį duomenų, prideda laiko žymę ir rašo rezultatą į „Output_chain.txt“. Grandinės transformacijos rodo srautų moduliškumą.
Geriausia srautų praktika „TypeScript“
- Įveskite savo duomenis: Apibrėžkite, kad gabaliukai anksti sugautų tipo klaidas.
- Tvarkyti klaidas: Visada pridėkite klaidų įvykių klausytojus, kad išvengtumėte neapdorotų išimčių.
- Protingai naudokite vamzdžius: Vamzdynai sumažina rankinio įvykių tvarkymą ir pagerina skaitomumą.
- Atgalinis slėgis: Norėdami gauti didelius duomenis, stebėkite „WriteStream.WRITERHIGHWETERMARK“, kad išvengtumėte tikslo.
Realaus pasaulio naudojimo atvejis: srautinio perdavimo API atsakymai
Įsivaizduokite, kad kuriate API, kuris transliuoja didelį duomenų rinkinį. Naudojant „Express“ ir „Stream“:
import express from 'express';
import { Readable } from 'stream';
const app = express();
app.get('/stream-data', (req, res) => {
const data = ('Item 1\n', 'Item 2\n', 'Item 3\n');
const stream = Readable.from(data);
res.setHeader('Content-Type', 'text/plain');
stream.pipe(res);
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});
Įdiekite priklausomybes („NPM Install Express @Type/Express“), tada paleiskite ją. Apsilankykite http: // localhost: 3000/Stream-Data, kad pamatytumėte duomenų srautą savo naršyklėje!
Išplėstiniai patarimai: tvarkymas atgal
Kai rašymo srautas negali neatsilikti nuo skaitomo srauto, atsiranda atgalinis slėgis. „Node.js“ tai automatiškai tvarko su vamzdžiais, tačiau galite jį stebėti rankiniu būdu:
const writeStream = createWriteStream('large_output.txt');
if (!writeStream.write('data')) {
console.log('Backpressure detected! Pausing...');
writeStream.once('drain', () => {
console.log('Resuming...');
});
}
Tai užtikrina, kad jūsų programa išliks reaguojanti esant didelėms apkrovoms.
Atsargumo priemonės, kaip naudoti atgalinį slėgį: Rašant didelius duomenų kiekius, skaitomas srautas gali gauti duomenis greičiau, nei gali suvartoti rašomas srautas. Nors vamzdis ir vamzdynas tai tvarko automatiškai, jei rašote rankiniu būdu, patikrinkite, ar rašymas () grąžina klaidingai, ir prieš rašydami daugiau laukite įvykio „Nutalpinimas“.
Be to, „Async“ iteratoriai (laukti … iš) yra modernios skaitomų srautų suvartojimo alternatyvos, kurios dažnai gali supaprastinti kodą, palyginti su .on ('duomenys') ir .on ('pabaiga').
Pavyzdys:
async function processStream(readable: Readable) {
for await (const chunk of readable) {
console.log('Chunk:', chunk);
}
console.log('Finished reading.');
}
Papildomi taškai:
Užtikrinkite išteklių valymą: Tai ypač svarbu įgyvendinant pasirinktinį srautą arba naudojant „Stream.pipeline“. Aiškiai skambinkite „Stream.Destroy“ () klaidų scenarijuose arba kai srautui nebereikia išleisti pagrindinių išteklių ir užkirsti kelią nuotėkiui. „Stream.pipeline“ automatiškai tvarko vamzdynų srautus.
Patogumui naudokite „Readable.From“ (): Kai jums reikia sukurti srautą iš esamo pasikartojančio (pvz., Masyvo) arba „Async“ pakartotinio, skaitomo.
Išvada
Srautai yra žaidimų keitiklis „Node.js“, o „TypeScript“ juos toliau sustiprina įvedant tipo saugą ir aiškumą. Nuo failų skaitymo iki duomenų transformavimo realiuoju laiku, srautų įsisavinimas atveria efektyvių I/O galimybių pasaulį. Čia pateikiami pavyzdžiai – perskaitymas, rašymas, keitimas, suspaudimas ir srautinis srautas per HTTP – sustiprina to, kas įmanoma, paviršių.
Eksperimentuokite su savo vamzdynais: pabandykite transliuoti žurnalus, apdoroti CSV failus arba sukurti tiesioginių pokalbių sistemą. Kuo daugiau tyrinėsite, tuo labiau įvertinsite srautų universalumą.