Understanding the filter Operator
The filter operator passes along every value from the source observable that meets the criteria defined by a predicate function. This behavior mirrors the classic filter method found on Array in JavaScript.
You supply the predicate as an argument, and it runs against each item emitted by the source. When the predicate evaluates to true, the value proceeds to the observer; when it evaluates to false, the value is discarded. The predicate also receives an index parameter that tracks how many values have been emitted by the source since subscription, beginning at 0.
For scenarios where you only want to skip consecutive duplicates, consider distinctUntilChanged. If your goal is to terminate the observable once a condition fails, takeWhile is the better fit.
The execution flow of filter unfolds as follows:
- Establish a subscription to the source observable
- On each incoming value, invoke the predicate with that value
- If the predicate returns
false, skip the value; otherwise, deliver it to the observer - When the source completes, forward the complete notification to the observer
- If the source throws an error, pass the error notification to the observer
Practical Application
Developers reach for filter constantly to pare down unnecessary emissions. Imagine you care only about click events on DIV elements. For performance, you attach a single listener to the document and then filter out any DOM nodes that are not DIV. The implementation looks like this:
const div = document.createElement('div');
const span = document.createElement('span');
document.body.appendChild(div);
document.body.appendChild(span);
fromEvent(document, 'click').pipe(
filter((event: any) => event.target.tagName === 'DIV')
).subscribe(event => console.log(event));
setTimeout(() => div.click(), 1000);
// this click event is filtered out and not emitted to the observer
setTimeout(() => span.click(), 2000);
