2.8 KiB
merge
(no submodules)
Purpose
merge combines multiple ReadableStream<T> instances into a single ReadableStream<T> that emits chunks in arrival order. It is a fan-in operation: the merged stream races all input readers simultaneously and yields whichever chunk becomes available first, interleaving outputs from all sources as they produce data. The merged stream closes when every input stream has closed.
This is useful for consuming multiple independent data sources concurrently — for example, merging results from several parallel API calls, aggregating events from multiple sensors, or combining log streams from different servers.
Features
- Race-based interleaving — faster streams yield chunks sooner
- Merged stream closes when all inputs close
- Error from any input stream errors the merged stream
- Handles empty input (returns immediately-closed stream)
- Single-stream input returns the stream unchanged
Usage: Basic Example
import { merge } from "@suaveplan/streams/pipeline/merge/merge";
import { fromArray } from "@suaveplan/streams/readable/from-array/from-array";
import { toArray } from "@suaveplan/streams/sinks/to-array/to-array";
const [a, b] = [fromArray([1, 3, 5]), fromArray([2, 4, 6])];
const merged = merge([a, b]);
const result = await toArray(merged);
// result contains 1,2,3,4,5,6 in some interleaved order
console.log(result.length); // 6
Usage: Advanced Example
import { merge } from "@suaveplan/streams/pipeline/merge/merge";
import { fromAsyncGenerator } from "@suaveplan/streams/readable/from-generator/from-generator";
import { toArray } from "@suaveplan/streams/sinks/to-array/to-array";
async function* slow() { yield "slow-1"; await new Promise(r => setTimeout(r, 10)); yield "slow-2"; }
async function* fast() { yield "fast-1"; yield "fast-2"; }
const result = await toArray(merge([
fromAsyncGenerator(slow),
fromAsyncGenerator(fast),
]));
console.log(result); // fast items likely come before slow items
API Reference
merge<T>(streams: ReadableStream<T>[]): ReadableStream<T>
Parameters:
streams— array of readable streams to merge.
Returns: ReadableStream<T> that emits all chunks from all inputs.
Returns immediately-closed stream if streams is empty.
Returns streams[0] unchanged if streams.length === 1.
Implementation Notes
Each input reader has an in-flight read() promise maintained in a Map. On each pull, Promise.race is called over all active in-flight promises to get the next available chunk. When a reader is done, it is removed from the active set. When all readers are done, the controller is closed. Errors from any reader propagate immediately to the merged stream's error channel, and all other readers are cancelled via Promise.allSettled in the cancel callback.