Transform y Duplex
Los streams Transform se sitúan en medio de los pipelines para analizar, comprimir o filtrar. Los streams Duplex modelan canales bidireccionales como los sockets con lados de lectura y escritura independientes.
Busca en todas las páginas de la documentación
Los streams Transform se sitúan en medio de los pipelines para analizar, comprimir o filtrar. Los streams Duplex modelan canales bidireccionales como los sockets con lados de lectura y escritura independientes.
import { Transform, PassThrough } from 'node:stream';
import { pipeline } from 'node:stream/promises';
const tap = new PassThrough();
tap.on('data', (chunk) => metrics.bytes(chunk.length));
await pipeline(source, tap, destination);Cuándo usarlo:
import { Transform, PassThrough, Duplex } from 'node:stream';
import { pipeline } from 'node:stream/promises';
import { Readable } from 'node:stream';
const splitLines = new Transform({
transform(chunk, _enc, cb) {
const parts = String(chunk).split('\n');
const tail = parts.pop() ?? '';
for (const line of parts) if (line) this.push(line);
cb(null, tail); // almacena la línea incompleta en el estado interno pasándola como chunk a la siguiente
},
flush(cb) {
cb();
},
});
function tee<T extends NodeJS.ReadableStream>(source: T): [T, PassThrough] {
const branch = new PassThrough();
source.pipe(branch);
return [source, branch];
}
// Duplex conceptual - net.Socket extiende Duplex
const socketLike = new Duplex({
read() { /* extrae del recurso subyacente */ },
write(chunk, _enc, cb) { cb(); /* envía al par */ },
});import { createGzip } from 'node:zlib';
await pipeline(
Readable.from(['{"a":1}\n', '{"b":2}\n']),
splitLines,
createGzip(),
process.stdout,
);Lo que esto demuestra:
PassThrough es un Transform de identidad, ideal para canales laterales tap/teezlib.createGzip() es un Transform subyacenteread/write de Duplex son independientes, a diferencia del flujo vinculado de Transform_transform por chunk y un _flush opcional al final._read y _write por separado - sockets TCP, TLS.| Necesidad | Tipo |
|---|---|
| gzip, cifrado | Transform |
| socket TCP | Duplex |
| métricas de tap | PassThrough |
| analizar líneas | Transform |
import { Transform, type TransformCallback } from 'node:stream';
function createJsonParser(): Transform {
let buffer = '';
return new Transform({
objectMode: true,
transform(chunk: Buffer, _enc: BufferEncoding, cb: TransformCallback) {
buffer += chunk.toString('utf8');
const lines = buffer.split('\n');
buffer = lines.pop() ?? '';
try {
for (const line of lines) if (line) this.push(JSON.parse(line));
cb();
} catch (err) {
cb(err as Error);
}
},
});
}cb(err) una vez._flush empuja el resto.| Alternativa | Cuándo usar | Cuándo NO usar |
|---|---|---|
readline | Archivos de texto basados en líneas | Marcos binarios |
| Mapa de generador asíncrono | Transformaciones pequeñas en memoria | Necesidad de contrapresión |
| Hilo de trabajador dedicado | Trabajo pesado de CPU por chunk | Operaciones de cadena ligeras |
| Fanout de cola de mensajes | Múltiples consumidores lentos | Solo en proceso |
La salida de Transform se deriva de la entrada en un pipeline. La lectura/escritura de Duplex son canales independientes.
Monitoreo, ramas tee, conexión de streams Web y Node con Duplex.fromWeb.
Dos ramas PassThrough o patrón multicast - observa la contrapresión en ambas.
Usa un generador asíncrono a través de Readable.from o llama a cb después de await en _transform cuidadosamente una vez.
Sí, createGzip, createGunzip son streams Transform.
pipeline(Readable.from([input]), transform, collectWritable) afirma los chunks de salida.
Se ejecuta cuando el upstream termina - emite los bytes/objetos finales almacenados en búfer.
Sí, a través de pipeline - el orden es fuente → analizar → comprimir → sumidero.
crypto.createCipheriv devuelve Transform - mismas reglas de error/pipeline.
Readable.fromWeb / Writable.toWeb unen los cuerpos de fetch y los pipelines de Node.
Usa Transform de bytes antes/después para convertir - por ejemplo, bytes → objetos JSON → bytes.
Evita toString por chunk en búferes enormes - opera en slices de Buffer cuando sea posible.
Versiones de la pila: Esta página fue escrita para Node.js 24.18.0 (LTS activa), npm 10+, TypeScript 5.6+, Express 5, Fastify 5 y NestJS 11.
Revisado por Chris St. John·Última actualización: 16 jul 2026