chapter ten

10 Streams

 

This chapter covers:

  • Comparing the three common streaming architectures in JavaScript
  • Distinguishing push streams from pull streams
  • Defining and tracing how backpressure works in each model
  • Measuring the per-chunk-of-data cost of each model
  • Identifying the patterns that quietly undermine stream performance

The previous chapters have each focused on a single aspect of JavaScript's hidden machinery: how data types work, how functions execute, how errors propagate, how the scheduling queues interact, how the garbage collector competes for the main thread, and so on. Streams are one example of a higher-order mechanism where all of these converge. A stream pipeline creates closures that capture scope, schedules callbacks through event emitters or promises, allocates objects for every chunk of data, and propagates errors across asynchronous boundaries. Every chapter in this book so far describes something that a stream pipeline does continuously, under load, for the lifetime of the data flow.

A stream is a way of moving data through an application one chunk at a time rather than all at once. Instead of loading an entire file, response, or query result into memory and then act on it, a stream hands your code one piece, lets you process it, and moves on to the next. Work can begin before all the data has arrived, and only a small working set is held in memory at any moment no matter how large the full dataset is.

10.1 You had one, er, three jobs

10.1.1 Push Streams

10.1.2 Pull Streams

10.2 Layers of nuance

10.3 Node.js streams internals

10.3.1 Readable and Writable

10.3.2 Pipe vs. pipeline

10.3.3 Transforms

10.4 Web streams internals

10.4.1 ReadableStream and the pull cycle

10.4.2 Backpressure through desiredSize

10.4.3 WritableStream and TransformStream

10.4.4 pipeTo() and pipeThrough()

10.4.5 The promise tax

10.5 Async iterables

10.5.1 Async generators

10.5.2 The hidden costs

10.5.3 The bridge to streams

10.6 Where the models diverge

10.6.1 Error propagation

10.6.2 The backpressure convergence

10.6.3 What the models actually cost

10.6.4 Conversion boundaries

10.7 Working against the grain

10.7.1 Async transforms on synchronous data

10.7.2 Accumulating data across chunks

10.7.3 Creating streams you don't need

10.8 Working with the grain

10.9 Summary