Operators are the building blocks you compose inside .pipe() to transform, filter, and combine Observable streams. This lesson covers the operators you will reach for most often in Angular apps.
What Are RxJS Operators?
An operator is a pure function that takes an Observable and returns a new Observable with some transformation applied, filtering values, mapping them to something else, or combining them with another stream, without mutating the original source.
Operators are chained together using .pipe(), reading top to bottom as the sequence of transformations a value goes through on its way to your subscriber.
This pipeline debounces typing, ignores duplicate terms, skips very short queries, cancels stale searches, and gracefully falls back to an empty list on error.
map/filter work like their array equivalents, but over time instead of over a fixed collection.
The *Map family (switchMap, mergeMap, concatMap, exhaustMap) each flatten inner Observables with different concurrency rules.
Combination operators (combineLatest, forkJoin, merge) merge multiple streams together in different ways.
catchError/retry handle failures within the pipeline itself.
RxJS Operators Cheatsheet
The operators you will use most in real Angular applications.
Operator
Purpose
map
Transform each emitted value
filter
Only let values passing a predicate through
tap
Perform a side effect without changing the value
debounceTime(ms)
Wait for a pause in emissions before emitting
distinctUntilChanged()
Skip consecutive duplicate values
switchMap
Flatten and cancel any previous inner Observable
mergeMap
Flatten and run inner Observables concurrently
concatMap
Flatten inner Observables sequentially, one at a time
catchError
Recover from or transform an error
take(n) / takeUntilDestroyed()
Limit how long a subscription stays active
switchMap vs mergeMap vs concatMap vs exhaustMap
These four operators all flatten an Observable-of-Observables into a single stream, but differ in how they handle overlapping inner Observables, a distinction that matters a lot for correctness in real apps.
Operator
Behavior When a New Value Arrives
switchMap
Cancels the previous inner Observable and switches to the new one
mergeMap
Keeps the previous inner Observable running, runs both concurrently
concatMap
Queues the new inner Observable until the current one finishes
exhaustMap
Ignores new values entirely while an inner Observable is still active
Example: A Debounced Search Box
This combination, debounceTime plus distinctUntilChanged plus switchMap, is one of the most common RxJS patterns in real Angular applications, and worth memorizing as a unit.
Use switchMap for "only the latest request matters" scenarios (search, live filters). Use concatMap when order and completeness matter (processing a queue of uploads one at a time). Use exhaustMap to prevent duplicate submissions, like ignoring extra clicks on a submit button while a request is still in flight.
Common Mistakes
Using mergeMap for a search box, allowing older, stale requests to resolve after (and overwrite) a newer one.
Using switchMap for a form submission handler, accidentally canceling an in-progress save if the user clicks submit twice.
Forgetting catchError inside a pipeline, letting a single failed emission terminate the whole subscription instead of a single request.
Overcomplicating a simple synchronous transformation with RxJS operators when a plain array .map()/.filter() would do.
Key Takeaways
Operators are composed inside .pipe() to transform, filter, and combine Observable streams.
switchMap, mergeMap, concatMap, and exhaustMap each flatten inner Observables with different concurrency semantics.
debounceTime + distinctUntilChanged + switchMap is the standard pattern for reactive search-as-you-type.
Choosing the wrong flattening operator is a common source of subtle race-condition bugs.
Pro Tip
When in doubt about which flattening operator to use, default to switchMap for read operations (searches, lookups) and exhaustMap for write operations you want to protect from duplicate submissions (like form saves).
You now understand the most useful RxJS operators. Next, learn about the Async Pipe, the cleanest way to render Observables directly in templates.