Skip to content

Los streams son una abstracción importante para trabajar con datos binarios sin cargarlos todos en memoria a la vez. Se usan comúnmente para leer y escribir archivos, enviar y recibir solicitudes de red, y procesar grandes cantidades de datos.

Bun implementa las APIs Web ReadableStream y WritableStream.

NOTE

Bun también implementa el módulo `node:stream`, incluyendo [`Readable`](https://nodejs.org/api/stream.html#stream_readable_streams), [`Writable`](https://nodejs.org/api/stream.html#stream_writable_streams), y [`Duplex`](https://nodejs.org/api/stream.html#stream_duplex_and_transform_streams). Para documentación completa, consulta la [documentación de Node.js](https://nodejs.org/api/stream.html).

Para crear un ReadableStream simple:

ts
const stream = new ReadableStream({
  start(controller) {
    controller.enqueue("hello");
    controller.enqueue("world");
    controller.close();
  },
});

El contenido de un ReadableStream se puede leer fragmento por fragmento con la sintaxis for await.

ts
for await (const chunk of stream) {
  console.log(chunk);
}

// hello
// world

ReadableStream Directo

Bun implementa una versión optimizada de ReadableStream que evita copias de datos innecesarias y lógica de gestión de colas.

Con un ReadableStream tradicional, los fragmentos de datos se encolan. Cada fragmento se copia en una cola, donde permanece hasta que el stream está listo para enviar más datos.

ts
const stream = new ReadableStream({
  start(controller) {
    controller.enqueue("hello");
    controller.enqueue("world");
    controller.close();
  },
});

Con un ReadableStream directo, los fragmentos de datos se escriben directamente en el stream. No ocurre ningún encolado y no es necesario clonar los datos del fragmento en memoria. La API del controller se actualiza para reflejar esto; en lugar de .enqueue() llamas a .write.

ts
const stream = new ReadableStream({
  type: "direct", 
  pull(controller) {
    controller.write("hello");
    controller.write("world");
  },
});

Al usar un ReadableStream directo, todo el encolado de fragmentos lo maneja el destino. El consumidor del stream recibe exactamente lo que se pasa a controller.write(), sin ninguna codificación o modificación.


Streams de generadores asíncronos

Bun también soporta funciones generadoras asíncronas como fuente para Response y Request. Esta es una forma fácil de crear un ReadableStream que obtiene datos de una fuente asíncrona.

ts
const response = new Response(
  (async function* () {
    yield "hello";
    yield "world";
  })(),
);

await response.text(); // "helloworld"

También puedes usar [Symbol.asyncIterator] directamente.

ts
const response = new Response({
  [Symbol.asyncIterator]: async function* () {
    yield "hello";
    yield "world";
  },
});

await response.text(); // "helloworld"

Si necesitas un control más granular sobre el stream, yield devolverá el controller directo del ReadableStream.

ts
const response = new Response({
  [Symbol.asyncIterator]: async function* () {
    const controller = yield "hello";
    await controller.end();
  },
});

await response.text(); // "hello"

Bun.ArrayBufferSink

La clase Bun.ArrayBufferSink es un escritor incremental rápido para construir un ArrayBuffer de tamaño desconocido.

ts
const sink = new Bun.ArrayBufferSink();

sink.write("h");
sink.write("e");
sink.write("l");
sink.write("l");
sink.write("o");

sink.end();
// ArrayBuffer(5) [ 104, 101, 108, 108, 111 ]

Para obtener los datos como Uint8Array, pasa la opción asUint8Array al método start.

ts
const sink = new Bun.ArrayBufferSink();
sink.start({
  asUint8Array: true, 
});

sink.write("h");
sink.write("e");
sink.write("l");
sink.write("l");
sink.write("o");

sink.end();
// Uint8Array(5) [ 104, 101, 108, 108, 111 ]

El método .write() soporta cadenas, arrays tipados, ArrayBuffer y SharedArrayBuffer.

ts
sink.write("h");
sink.write(new Uint8Array([101, 108]));
sink.write(Buffer.from("lo").buffer);

sink.end();

Una vez que se llama a .end(), no se pueden escribir más datos en el ArrayBufferSink. Sin embargo, en el contexto de almacenar en búfer un stream, es útil escribir datos continuamente y periódicamente hacer .flush() del contenido (por ejemplo, en un WritableStream). Para soportar esto, pasa stream: true al constructor.

ts
const sink = new Bun.ArrayBufferSink();
sink.start({
  stream: true, 
});

sink.write("h");
sink.write("e");
sink.write("l");
sink.flush();
// ArrayBuffer(5) [ 104, 101, 108 ]

sink.write("l");
sink.write("o");
sink.flush();
// ArrayBuffer(5) [ 108, 111 ]

El método .flush() devuelve los datos almacenados en búfer como un ArrayBuffer (o Uint8Array si asUint8Array: true) y limpia el búfer interno.

Para establecer manualmente el tamaño del búfer interno en bytes, pasa un valor para highWaterMark:

ts
const sink = new Bun.ArrayBufferSink();
sink.start({
  highWaterMark: 1024 * 1024, // 1 MB
});

Referencia

ts
/**
 * Escritor incremental rápido que se convierte en un `ArrayBuffer` en end().
 */
export class ArrayBufferSink {
  constructor();

  start(options?: {
    asUint8Array?: boolean;
    /**
     * Preasignar un búfer interno de este tamaño
     * Esto puede mejorar significativamente el rendimiento cuando el tamaño del fragmento es pequeño
     */
    highWaterMark?: number;
    /**
     * En {@link ArrayBufferSink.flush}, devolver los datos escritos como un `Uint8Array`.
     * Las escrituras se reiniciarán desde el principio del búfer.
     */
    stream?: boolean;
  }): void;

  write(chunk: string | ArrayBufferView | ArrayBuffer | SharedArrayBuffer): number;
  /**
   * Vaciar el búfer interno
   *
   * Si {@link ArrayBufferSink.start} recibió una opción `stream`, esto devolverá un `ArrayBuffer`
   * Si {@link ArrayBufferSink.start} recibió una opción `stream` y `asUint8Array`, esto devolverá un `Uint8Array`
   * De lo contrario, esto devolverá el número de bytes escritos desde el último vaciado
   *
   * Esta API podría cambiar más tarde para separar Uint8ArraySink y ArrayBufferSink
   */
  flush(): number | Uint8Array<ArrayBuffer> | ArrayBuffer;
  end(): ArrayBuffer | Uint8Array<ArrayBuffer>;
}

Bun por www.bunjs.com.cn editar