Потоки (streams): обзор
Как Node обрабатывает гигабайты данных, не загружая их целиком в память.
Поток (stream) — это способ обрабатывать данные по частям (кусками-чанками) по мере их поступления, а не целиком.
Проблема: файл больше памяти
Представьте файл лога на 5 ГБ. Если прочитать его через fs.readFile, Node попытается загрузить все 5 ГБ в оперативную память — и приложение упадёт. Потоки решают это: они подают данные кусками, и в памяти в каждый момент лежит лишь небольшой фрагмент.
Аналогия: чтобы перелить воду из бочки, не нужно поднимать всю бочку — достаточно открыть кран и пускать воду струёй.
Четыре типа потоков
| Тип | Что делает | Пример |
| Readable | читает данные | чтение файла, тело запроса |
| Writable | пишет данные | запись файла, ответ сервера |
| Duplex | читает и пишет | сетевой сокет |
| Transform | преобразует на лету | сжатие, шифрование |
Чтение потоком
Поток оповещает о событиях: data (пришёл очередной кусок), end (данные закончились), error (что-то сломалось):
const fs = require("fs");
const stream = fs.createReadStream("big.log", "utf8");
let chunks = 0;
stream.on("data", (chunk) => {
chunks++;
console.log("Получен кусок размером", chunk.length);
});
stream.on("end", () => {
console.log("Готово, всего кусков:", chunks);
});
stream.on("error", (err) => {
console.error("Ошибка:", err.message);
});
Память расходуется только на один кусок за раз, а не на весь файл.
pipe — соединяем потоки
Самая выразительная вещь — метод pipe: он «перенаправляет» данные из читающего потока в пишущий. Копирование файла потоком умещается в одну строку:
const fs = require("fs");
fs.createReadStream("source.txt")
.pipe(fs.createWriteStream("copy.txt"));
// файл копируется кусками, память почти не тратится
Так же сервер может отдавать большой файл клиенту: читать его потоком и сразу «лить» в ответ через pipe, не держа целиком в памяти.
Обработка по кусочкам — это и есть суть
Идею «накапливать результат, обрабатывая поток частями» легко показать на чистом JS: считаем длину «потока» по приходящим кускам, не собирая их в одну строку:
// имитация кусков, приходящих из потока
const chunks = ["Привет", ", ", "поток", "ы", "!"];
let totalLength = 0;
for (const chunk of chunks) {
totalLength += chunk.length; // обрабатываем кусок и забываем его
}
console.log("Кусков:", chunks.length);
console.log("Суммарная длина:", totalLength);
Вывод:
Кусков: 5 Суммарная длина: 15
Итог
- Потоки обрабатывают данные по частям, не загружая всё в память.
- Незаменимы для больших файлов и сетевых данных.
- Четыре типа: Readable, Writable, Duplex, Transform.
pipeсоединяет потоки — например, копирует файл одной строкой.