What is backpressure, and how do streams handle it?
Quick Answer
Backpressure is when a fast Readable produces data quicker than a slow Writable can consume it. `write()` returns false when the internal buffer (highWaterMark) is full; you should pause until a 'drain' event. `pipe()` and `pipeline()` handle this automatically, which is why they're preferred over manual data/write loops.
Detailed Answer
Answer: Backpressure occurs when a data source outpaces the destination — e.g., reading a file from a fast SSD and writing to a slow network socket. Without handling it, data piles up in memory and can exhaust it.
How Writable streams signal it:
writable.write(chunk)returnsfalsewhen the internal buffer has exceeded itshighWaterMark(default 16 KB for byte streams).- When you get
false, you should stop writing and wait for the'drain'event before resuming.
Manual handling (illustrative):
readable.on('data', (chunk) => {
const ok = writable.write(chunk);
if (!ok) {
readable.pause(); // stop reading
writable.once('drain', () => readable.resume()); // resume when drained
}
});
The right way — let Node manage it:
const { pipeline } = require('stream/promises');
await pipeline(
fs.createReadStream('huge.log'),
zlib.createGzip(),
fs.createWriteStream('huge.log.gz')
);
// pipeline handles backpressure AND propagates errors + cleans up
pipe()andpipeline()automatically pause/resume the source based on the destination's readiness.pipeline()additionally forwards errors and destroys all streams on failure (avoiding leaks) — prefer it overpipe()for anything beyond trivial cases.
Interview point: backpressure is the reason to use pipe/pipeline instead of hand-rolling on('data') + write(); manual loops without drain handling leak memory under load.