Understanding bufferTime

The bufferTime operator gathers values emitted by the source observable into a temporary storage without forwarding them immediately to the subscriber. This accumulation continues until a predefined time window expires. At that moment, the stored values are released as a single array, the storage is cleared, and the cycle repeats with a fresh time window.

This operator bears resemblance to buffer, yet it distinguishes itself by relying on a fixed time duration rather than another observable to trigger the flushing of the buffer.

A notable characteristic of bufferTime is its ability to emit an empty array to the observer when the time window expires without any values being collected during that period.

The operational flow of this operator can be summarized as follows:

  1. Establish a subscription to the source observable
  2. Initialize a timer for the specified duration
  3. Upon receiving a new value from the source, store it in the buffer
  4. When the timer completes, pass all buffered values to the observer as a single emission, regardless of whether the buffer holds any data
  5. Restart the process from step 2
  6. Upon source completion, deliver the complete notification to the observer
  7. If the source emits an error, forward the error notification to the observer.

A more advanced variation of this operator enables selective value inclusion. This is achieved by supplying a creation interval parameter as shown below:

const bufferCreationInterval = 1000;
const bufferTimeSpan = 500;
interval(200).pipe(
   take(10),
   // this setup will ignore values 2, 3, 7, 8
   bufferTime(bufferTimeSpan, bufferCreationInterval)
).subscribe((v) => console.log(v));

When the creation interval is introduced, the operator behaves as follows:

  1. Establish a subscription to the source observable
  2. Initiate both the time span and creation interval timers
  3. Upon receiving a new value from the source, store it in the buffer while the time span timer is active; otherwise, disregard it
  4. When the time span timer completes, pass all buffered values to the observer as a single emission, even if the buffer is empty
  5. When the creation interval timer completes, resume from step 2
  6. Upon source completion, deliver the complete notification to the observer
  7. If the source emits an error, forward the error notification to the observer.

Practical Application

bufferTime proves valuable in scenarios requiring batching. For instance, when a highly resource-intensive operation must be executed repeatedly in rapid succession—such as refreshing a DOM tree in response to stream updates—batching enables the aggregation of these updates and their simultaneous processing.

Consider this example that utilizes bufferTime to output an array containing the most recent clicks:

const clicks = fromEvent(document, 'click');
const buffered = clicks.pipe(bufferTime(1000));
buffered.subscribe(x => console.log(x));

Interactive Demo

Further Reading