Ответ
Потоки (Streams) в Node.js — это абстракции для работы с последовательными данными, которые позволяют обрабатывать информацию по частям (чанкам), не загружая весь объём в память. Это фундаментальная концепция для эффективной работы с I/O операциями.
Типы потоков:
- Readable: Поток для чтения (например,
fs.createReadStream, HTTP-запрос). - Writable: Поток для записи (например,
fs.createWriteStream, HTTP-ответ). - Duplex: Читаемый и записываемый одновременно (например, TCP-сокет).
- Transform: Разновидность Duplex, которая может модифицировать данные (например,
zlib.createGzip).
Практические примеры:
-
Копирование больших файлов с контролем памяти:
const fs = require('fs'); const path = require('path'); const source = path.join(__dirname, 'large-video.mp4'); const destination = path.join(__dirname, 'copy-video.mp4'); const readStream = fs.createReadStream(source); const writeStream = fs.createWriteStream(destination); // Pipe автоматически управляет backpressure readStream.pipe(writeStream); writeStream.on('finish', () => { console.log('Файл успешно скопирован потоком.'); }); writeStream.on('error', (err) => { console.error('Ошибка записи:', err); });Без потоков пришлось бы использовать
fs.readFile, который загрузил бы весь файл в память. -
Обработка данных на лету (Transform Stream):
const { Transform } = require('stream'); const fs = require('fs'); // Создаём кастомный трансформационный поток для шифрования class EncryptStream extends Transform { constructor(shift) { super(); this.shift = shift; } _transform(chunk, encoding, callback) { // Простой шифр Цезаря для примера const encrypted = chunk.toString().split('').map(char => { const code = char.charCodeAt(0); return String.fromCharCode(code + this.shift); }).join(''); this.push(encrypted); callback(); } } const encryptor = new EncryptStream(3); const reader = fs.createReadStream('secret.txt'); const writer = fs.createWriteStream('secret.encrypted.txt'); reader.pipe(encryptor).pipe(writer); -
Обработка HTTP-запросов/ответов:
const http = require('http'); const fs = require('fs'); const server = http.createServer((req, res) => { // Отправка большого файла клиенту потоком const fileStream = fs.createReadStream('./large-data.json'); res.writeHead(200, { 'Content-Type': 'application/json' }); fileStream.pipe(res); // Данные отправляются чанками по мере чтения fileStream.on('error', (err) => { res.statusCode = 500; res.end('Internal Server Error'); }); });
Ключевые преимущества потоков в Node.js:
- Эффективное использование памяти: Обработка данных чанками (по умолчанию 64KB) вместо загрузки гигабайтных файлов целиком.
- Улучшенная производительность и отзывчивость: Данные можно начинать обрабатывать или отправлять сразу после получения первого чанка, не дожидаясь окончания передачи.
- Контроль backpressure: Механизм
pipe()автоматически приостанавливает чтение, если запись не успевает, предотвращая переполнение памяти. - Композиция: Потоки легко соединяются в конвейеры (pipeline) для сложной обработки (чтение -> распаковка -> парсинг -> запись).