Skip to content

Filtering Operators (debounceTime, distinctUntilChanged)

Filtering operators control which values pass through the stream, often used for performance optimization and preventing unnecessary processing.

Waits for a specified time span of silence (no new values) and then emits the latest value. Great for search inputs to avoid firing requests on every keystroke.

import { fromEvent } from "rxjs";
import { debounceTime, map } from "rxjs/operators";
const input = document.getElementById("search");
const input$ = fromEvent(input, "input").pipe(
map((event) => (event.target as HTMLInputElement).value),
debounceTime(300), // wait 300ms after last keystroke
);
input$.subscribe((value) => console.log("Search for:", value));

Suppresses duplicate consecutive values. Only emits if the current value is different from the previous one. Can also use a custom comparator.

import { of } from "rxjs";
import { distinctUntilChanged } from "rxjs/operators";
of(1, 1, 2, 2, 2, 1, 1, 3).pipe(distinctUntilChanged()).subscribe(console.log); // 1,2,1,3

In Angular, you often combine it with debounceTime for search inputs to avoid identical queries:

searchInput.valueChanges.pipe(debounceTime(300), distinctUntilChanged()).subscribe((query) => this.search(query));

Emits the first value, then ignores subsequent values for a specified duration. Useful for rate‑limiting events like scroll or resize.

import { fromEvent } from "rxjs";
import { throttleTime } from "rxjs/operators";
fromEvent(window, "scroll")
.pipe(throttleTime(1000))
.subscribe(() => console.log("Scrolled (at most once per second)"));

Emits the most recent value from the source within periodic time intervals.

import { fromEvent } from "rxjs";
import { sampleTime, map } from "rxjs/operators";
fromEvent(document, "mousemove")
.pipe(
sampleTime(1000),
map((event) => ({ x: event.clientX, y: event.clientY })),
)
.subscribe((position) => console.log("Mouse position each second:", position));

Similar to debounceTime but uses a different timing strategy: it starts a timer on the first value, and emits the latest value when the timer ends. Good for things like saving drafts.

import { fromEvent } from "rxjs";
import { auditTime } from "rxjs/operators";
fromEvent(input, "input")
.pipe(auditTime(2000))
.subscribe(() => console.log("Save draft"));

skip(n) ignores the first n emissions; take(n) takes only the first n.

import { interval } from "rxjs";
import { skip, take } from "rxjs/operators";
interval(1000)
.pipe(
skip(2), // skip 0,1
take(3), // take 2,3,4
)
.subscribe(console.log);
OperatorBehavior
debounceTimeEmits after a quiet period
distinctUntilChangedEmits only when value changed from previous
throttleTimeEmits first, then ignores for a duration
sampleTimeEmits latest value periodically
auditTimeEmits latest after a duration from first value

Choose the right operator to optimize your streams and reduce unnecessary emissions.