Glossary / Readable streams

Readable streams

A ReadableStream exposes data produced over time through a source and controller. Consumers can read chunks with a reader, iterate asynchronously where supported, or pipe data to another stream; the design enables incremental processing, cancellation, and backpressure.

Underlying source and consumer modes

An underlying source may implement start, pull, and cancel. start runs during setup. pull is called when the queue needs data; it can return a promise to represent asynchronous production. cancel(reason) lets the source release resources. The controller enqueues chunks, closes the stream, or errors it. A queuing strategy defines a high-water mark and optionally the size of each chunk.

const reader = stream.getReader(); // locks stream
try {
  for (;;) {
    const { value, done } = await reader.read();
    if (done) break;
    await consume(value);
  }
} finally {
  reader.releaseLock();
}

A default reader yields chunks as values. A BYOB reader can let a byte source fill caller-provided buffers, reducing allocations in suitable binary pipelines. A locked stream cannot be read or piped by another consumer until the lock is released. Async iteration is convenient, but early loop exit should be understood in terms of iterator cancellation/stream ownership.

Completion, cancellation, and failure

read() resolves with { value, done }; done: true marks normal completion. If the stream errors, pending and future reads reject. cancel(reason) asks the source to stop and discards queued data. Releasing a reader lock does not itself cancel the source. Piping has its own propagation rules, and preventCancel, preventClose, and preventAbort options alter those relationships.

Chunks may split records at arbitrary positions. For text protocols, use an incremental decoder such as TextDecoder with streaming mode and carry incomplete record state between chunks. Do not decode each byte chunk independently if a multibyte character can span chunks.

Engineering guidance

Use pull to respect demand rather than eagerly enqueueing an unbounded source. Ensure cancellation actually stops network, file, timer, or producer work. Test empty input, one chunk, many partial chunks, source failure, consumer failure, and early cancellation. Be explicit about who owns the stream and when the lock is released.

Interview questions

  1. What is the difference between releasing a reader lock and canceling? Release permits another consumer; cancel asks the source to stop and discards queued data.
  2. What triggers pull? Demand and queue capacity, subject to the stream's queuing strategy.
  3. Why not assume each chunk is a full line? Chunk boundaries are arbitrary; maintain parsing state across reads.
  4. When is BYOB useful? For byte streams when filling caller-owned buffers can reduce allocation and copying.
  5. What happens when a stream is locked? Other readers and piping operations cannot acquire it until the lock is released.

References