ReadableStream creation
A ReadableStream can be created with a start function that receives a controller. The controller has an enqueue method to add data and a close method to signal the end of the stream. Example: const stream = new ReadableStream({ start(controller) { controller.enqueue("hello"); controller.enqueue("world"); controller.close(); } });
ReadableStream iteration
Read a stream chunk-by-chunk with for await: for await (const chunk of stream) { console.log(chunk); }. This iterates over each chunk enqueued to the stream.
Bun stream conversion functions
Bun implements optimized convenience functions for converting a ReadableStream to various binary formats. Bun.readableStreamToArrayBuffer(stream) - converts to ArrayBuffer. Bun.readableStreamToBytes(stream) - converts to Uint8Array. Bun.readableStreamToText(stream) - converts to string as UTF-8. Bun.readableStreamToArray(stream) - resolves to an array of chunks, where each chunk may be a string, typed array, or ArrayBuffer.
Split ReadableStream with tee
To split a ReadableStream into two streams that can be consumed independently: const [a, b] = stream.tee();
ReadableStream and WritableStream Web APIs
Bun implements the Web APIs ReadableStream and WritableStream for working with binary data without loading it all into memory at once. They are commonly used for reading and writing files, sending and receiving network requests, and processing large amounts of data.
Node.js stream module support
Bun implements the node:stream module, including Readable, Writable, and Duplex streams. Complete documentation is available in the Node.js docs.
ReadableStream basic creation and iteration
Create a ReadableStream by passing an object with a start method to the constructor. Call controller.enqueue() to add chunks of data and controller.close() to end the stream. Contents can be read chunk-by-chunk using for await syntax.
Direct ReadableStream optimization
Bun implements an optimized direct ReadableStream that avoids unnecessary data copying and queue management. Create a direct ReadableStream by setting type: "direct" in the constructor options. Use controller.write() instead of controller.enqueue(), and chunks are written directly to the stream without copying into a queue.
Direct ReadableStream backpressure handling
When using a direct ReadableStream, controller.write() returns the number of bytes written or a pending Promise<number> when the destination's internal buffer is full. Await the result to handle backpressure. Alternatively, use await controller.flush(true) after a write that returns a Promise. For default ReadableStreams and async-generator response bodies, Bun applies backpressure automatically by pausing the producer while the destination is backed up.
Direct ReadableStream with backpressure example
const stream = new ReadableStream({
type: "direct",
async pull(controller) {
for (const chunk of chunks) {
await controller.write(chunk);
}
controller.close();
},
});
Async generator functions as stream source
Bun supports async generator functions as a source for Response and Request. Use async generators to create a ReadableStream that fetches data from an asynchronous source. The generator function can use yield to produce chunks of data.
Async generator response body example
const response = new Response(
(async function* () {
yield "hello";
yield "world";
})(),
);
await response.text(); // "helloworld"
Symbol.asyncIterator for response bodies
Response and Request can use an object with Symbol.asyncIterator method as a body source. This allows creating a ReadableStream from any object that implements the async iterator protocol.
Symbol.asyncIterator response body example
const response = new Response({
[Symbol.asyncIterator]: async function* () {
yield "hello";
yield "world";
},
});
await response.text(); // "helloworld"
Async generator yield returns controller
In an async generator used as a response body, yield returns the direct ReadableStream controller, allowing manual control over stream behavior. Call controller.end() to terminate the stream early.
Async generator yield controller example
const response = new Response({
[Symbol.asyncIterator]: async function* () {
const controller = yield "hello";
await controller.end();
},
});
await response.text(); // "hello"
Bun.readableStreamTo*() conversion functions
Bun implements convenience functions for consuming ReadableStream bodies: readableStreamToArrayBuffer(), readableStreamToBytes() (returns Uint8Array), readableStreamToBlob(), readableStreamToJSON(), readableStreamToText(), readableStreamToArray() (returns unknown[]), readableStreamToFormData(stream) for x-www-form-urlencoded, readableStreamToFormData(stream, multipartFormBoundary) for multipart/form-data.