Работали ли вы с Stream API в Node.js?

Ответ

Да, активно использовал Stream API для обработки больших файлов и сетевых данных. Основные типы стримов: Readable, Writable, Duplex и Transform. Например, для обработки логов большого размера я использовал fs.createReadStream в паре с Transform стримом для парсинга и Writable для записи агрегированных результатов.

const fs = require('fs');
const { Transform } = require('stream');

const logParser = new Transform({
  transform(chunk, encoding, callback) {
    const lines = chunk.toString().split('n');
    const errors = lines.filter(line => line.includes('ERROR'));
    this.push(errors.join('n'));
    callback();
  }
});

fs.createReadStream('app.log')
  .pipe(logParser)
  .pipe(fs.createWriteStream('errors.log'));

Также использовал pipeline для безопасного соединения стримов с автоматической обработкой ошибок и очисткой ресурсов. Stream API критически важен для эффективной работы с I/O в Node.js, так как позволяет не блокировать Event Loop и экономить память.

Ответ 18+ 🔞

О, стримы в Node.js! Это же, блядь, как водопровод в старом доме — если правильно собрал, всё течёт гладко, а если косякнул — затопит всех соседей снизу, и будешь потом выгребать говно ведрами.

Вот смотри, я с этими штуками работал не раз, особенно когда приходилось большие файлы обрабатывать — логи там, дампы баз, всякую хрень. Там же, ёпта, если весь файл в память грузить, можно так овердохуища оперативки сожрать, что сервер на колени встанет и начнёт молиться о пощаде. Поэтому стримы — это наше всё.

Основные типы этих самых стримов, которые надо знать, чтобы не выглядеть полным ебаньком:

  • Readable — откуда данные текут. Как кран открыл.
  • Writable — куда данные вливаются. Ведро подставил.
  • Duplex — и туда, и обратно может. Как телефонная трубка, вроде.
  • Transform — самый интересный, хитрая жопа. Принимает данные, как-то их меняет и дальше отправляет. Фильтр там или компрессор.

Вот, например, был у меня случай — нужно было из гигабайтного лога приложения только строки с ошибками выцепить. Делать это в лоб, читая файл целиком — это, ядрёна вошь, самоубийство. Поэтому я использовал fs.createReadStream, чтобы читать по кусочкам, как из шланга воду отхлёбывать.

const fs = require('fs');
const { Transform } = require('stream');

const logParser = new Transform({
  transform(chunk, encoding, callback) {
    const lines = chunk.toString().split('n');
    const errors = lines.filter(line => line.includes('ERROR'));
    this.push(errors.join('n'));
    callback();
  }
});

fs.createReadStream('app.log')
  .pipe(logParser)
  .pipe(fs.createWriteStream('errors.log'));

Видишь, что происходит? Читающий стрим (createReadStream) качает данные из файла. Потом они попадают в мой Transform стрим — logParser. Этот чувак — настоящий трудяга. Он берёт каждый пришедший кусок (chunk), превращает в текст, режет на строки и оставляет только те, где есть "ERROR". Эти отфильтрованные ошибки он проталкивает дальше по трубе. А в конце Writable стрим (createWriteStream) аккуратненько складывает их в новый файл. Всё элегантно, память не ебёт, Event Loop свободен.

Но это, конечно, база. Когда пайплайны становятся сложнее, просто pipe может подвести. Один стрим ошибку выдаст, а остальные так и будут впустую воду лить, ресурсы держать. Поэтому умные дяди придумали pipeline. Эта штука, блядь, как ответственный прораб на стройке. Она соединяет стримы, следит, чтобы если один котелёк накрылся медным тазом, все остальные тоже корректно закрылись, ошибку наверх пробросила. Красота, а не функция. Без неё чувствуешь себя голым в метро — вроде идёшь, но волнение ебать, что сейчас что-то пойдёт не так.

В общем, Stream API в Node.js — это не просто фича для красоты. Это, сука, фундаментальная вещь для эффективной работы с вводом-выводом. Позволяет не блокировать главный цикл и не жрать память, как не в себя. Освоил это — уже на полпути к тому, чтобы не писать говнокод, который падает от первого же гигабайтного файла.