JavaScript ReadableStream: A Friendly Beginner
If you’ve ever watched water flow through a garden hose, you already understand the core idea behind ReadableStream in JavaScript: data arrives in small chunks, continuously, so you can start using it before the entire payload is ready.
Below you’ll find the shortest, simplest examples that run in any modern browser (Chrome, Edge, Firefox, Safari). Open DevTools → Console, paste the snippets, and play.
1. Create a ReadableStream in 5 Lines
// 1. Create a stream that pushes three strings
const stream = new ReadableStream({
start(controller) {
controller.enqueue("Hello ");
controller.enqueue("Readable");
controller.enqueue("Stream!");
controller.close(); // No more data
}
});
// 2. Read it
const reader = stream.getReader();
reader.read().then(({ value, done }) => console.log(value)); // → "Hello "
2. Read the Whole Stream with a Simple Loop
async function printStream(s) {
const reader = s.getReader();
while (true) {
const { value, done } = await reader.read();
if (done) break;
console.log(value);
}
}
printStream(stream);
3. Convert a Fetch Response into a Stream
You don’t have to build streams yourself—fetch already gives you one:
// Stream a remote file in small chunks
fetch("https://jsonplaceholder.typicode.com/posts")
.then(res => {
const reader = res.body.getReader();
return new ReadableStream({
start(controller) {
function pump() {
return reader.read().then(({ done, value }) => {
if (done) {
controller.close();
return;
}
controller.enqueue(value); // push chunk
return pump(); // read next chunk
});
}
return pump();
}
});
})
.then(stream => new Response(stream))
.then(res => res.text())
.then(console.log); // full text of all posts
4. Pipe a Stream to a WritableStream (One-Liner)
// Pipe console output to a textarea on the page
const { readable, writable } = new TransformStream();
readable.pipeTo(new WritableStream({
write(chunk) {
document.querySelector("textarea").value += new TextDecoder().decode(chunk);
}
}));
writable.getWriter().write(new TextEncoder().encode("Piped!\n"));
5. Backpressure? No Problem!
Streams automatically slow down the producer when the consumer is busy.
const slow = new ReadableStream({
start(c) {
let n = 0;
const id = setInterval(() => {
c.enqueue(++n);
if (n === 5) {
clearInterval(id);
c.close();
}
}, 1000);
}
});
// Consumer reads only when it feels like it
const reader = slow.getReader();
setInterval(async () => {
const { value } = await reader.read();
console.log("Got:", value);
}, 2000); // half the speed of the producer
TL;DR Cheat Sheet
| Task | Snippet |
|---|---|
| Create a stream | new ReadableStream({ start(c) { … } }) |
| Read once | stream.getReader().read().then(...) |
| Read all | while(true){ await reader.read() } |
| Pipe to writable | readable.pipeTo(writable) |
Ready to Explore?
ReadableStreams are the building blocks for file downloads, video streaming, compression, and more. Once you master these 5 snippets, you’re ready to dig into TransformStream, ReadableStreamBYOBReader, and Node.js equivalents.
Happy streaming!