Course contentsShow
← Node.js
Lesson 453 of 1,9805. Streams and BackpressureFree lesson

Stateful Transformations

Create stream transforms that maintain internal state to perform aggregate or windowed calculations.

Mastering Stateful Transformations

In this lesson, you will learn how to build stream transforms that "remember" previous chunks of data to perform calculations that depend on the history of the stream, not just the current piece.

The Problem with Statelessness

Most Transform streams are stateless; they treat every piece of data in isolation. If you want to calculate a running total or group items into windows, a standard stateless transform won't suffice because it forgets everything the moment it finishes processing a chunk. By introducing internal state, we turn our stream into a persistent processor.

Implementing State

To create a stateful transform, you simply initialize a variable inside the class (or factory function) that persists across _transform calls.

Imagine you are receiving a stream of numbers and want to calculate a running average. You need to keep track of the sum and the count of numbers processed so far.

const { Transform } = require('stream');

class RunningAverage extends Transform {
  constructor() {
    super({ objectMode: true });
    this.sum = 0;
    this.count = 0;
  }

  _transform(number, encoding, callback) {
    this.sum += number;
    this.count += 1;
    
    // Push the current average downstream
    const average = this.sum / this.count;
    this.push(average);
    
    callback();
  }
}

Why State Matters

Because the sum and count are defined within the scope of the class instance, they remain available throughout the entire lifecycle of the stream. This allows you to perform complex operations like:

  • Windowing: Accumulating chunks until a specific threshold is met before pushing.
  • Debouncing/Throttling: Storing the timestamp of the last processed event.
  • Deduplication: Keeping a Set or Map of seen IDs to filter out duplicates in real-time.

When using state, always remember that you are responsible for the memory footprint. If your state grows indefinitely (like a Set of IDs that never clears), you will eventually trigger a memory leak. Always consider if your state needs to be cleared or capped as part of your transformation logic.

Key Takeaway: Stateful transformations allow your stream to maintain context, enabling complex logic like running aggregates and windowing by storing data directly within the stream instance.