RxJS
RxJS Operators Guide with Examples
A practical RxJS guide focused on the most important operators, with beginner-friendly explanations, real use cases, Angular patterns, examples, traps, and practice questions.
Operators
Observables
Angular
Examples
Practice Questions
Published: Dec 2019 Updated: May 2026
Quick Summary
RxJS is: a library for working with values over time using Observables.
Operator means: a function that takes an Observable and returns a new Observable.
Use RxJS when: values arrive over time, can be cancelled, combined, filtered, retried, shared, or transformed.
Common examples: search input, button clicks, route changes, HTTP calls, WebSockets, timers, forms, and app state.
Biggest skill: choosing the right operator for the user experience.
Mental model: source Observable -> operators inside pipe() -> subscriber or async pipe.
Operator Categories
Category Important operators Purpose
Creation of, from, fromEvent, interval, timer, defer, throwErrorCreate Observable sources.
Transformation map, pluck, scan, reduce, bufferTime, pairwiseChange emitted values.
Filtering filter, take, first, last, skip, distinctUntilChanged, debounceTime, throttleTimeControl which values pass through.
Combination combineLatest, withLatestFrom, forkJoin, zip, merge, concat, raceWork with multiple streams.
Flattening switchMap, mergeMap, concatMap, exhaustMapMap a value to another Observable and flatten the result.
Error and retry catchError, retry, retryWhen, finalizeRecover, retry, and clean up.
Multicasting share, shareReplay, publishShare subscriptions and cached values.
Creation Operators
Operator Explanation Example use
ofCreates an Observable from fixed values. Return fallback data such as of([]).
fromCreates an Observable from array, promise, iterable, or similar source. Convert a Promise or array into a stream.
fromEventCreates a stream from DOM events. Listen to clicks, input, scroll, or keyup.
intervalEmits numbers repeatedly after a fixed interval. Polling, timer ticks, live counters.
timerEmits after a delay, optionally repeatedly. Delayed action or scheduled polling.
deferCreates a fresh Observable factory for each subscription. Run current-time logic only when subscribed.
throwErrorCreates an Observable that errors. Return an error from inside an operator.
Creation examples
Copy
creation-operators.ts
import { of, from, fromEvent, interval, timer, defer } from 'rxjs';
of('Angular', 'React', 'Vue');
from(fetch('/api/users'));
fromEvent(document, 'click');
interval(1000);
timer(2000);
defer(() => of(new Date().toISOString()));
Filtering and Rate-Control Operators
Operator Explanation Use when
filterPasses values matching a condition. Ignore invalid form values.
takeTakes a fixed number of values, then completes. Read first 1 or first N values.
takeUntilCompletes when another Observable emits. Cleanup on destroy or cancellation signal.
firstEmits the first matching value. Need first valid event.
skipIgnores the first N values. Ignore initial default state.
distinctUntilChangedSkips same consecutive value. Do not repeat search for same term.
debounceTimeWaits for quiet time before emitting. Search box after typing stops.
throttleTimeEmits at most once per time window. Button click or scroll rate limiting.
auditTimeEmits latest value after each time window. UI updates during scroll/resize.
sampleTimeSamples latest value at intervals. Periodic snapshot of frequent events.
Search input stream
Copy
search.component.ts
searchResults$ = fromEvent<InputEvent>(searchInput, 'input').pipe(
map(event => (event.target as HTMLInputElement).value.trim()),
filter(term => term.length >= 2),
debounceTime(300),
distinctUntilChanged(),
switchMap(term => this.api.search(term))
);
Combination Operators
Operator Explanation Best use
combineLatestCombines latest values from multiple streams after each has emitted. View model from filters, sort, page, and data.
withLatestFromMain stream emits while sampling latest values from others. Button click uses latest form state.
forkJoinWaits for all inner Observables to complete, then emits final values. Parallel HTTP calls needed once.
zipPairs values by emission order. Step-by-step pair matching.
mergeRuns streams together and emits as values arrive. Combine multiple event sources.
concatRuns streams one after another. Sequence tasks in order.
raceUses the stream that emits first. Timeout or fastest source wins.
Build a view model
Copy
dashboard.vm.ts
viewModel$ = combineLatest([
users$,
searchTerm$,
selectedRole$
]).pipe(
map(([users, term, role]) => ({
users: users.filter(user =>
user.name.includes(term) && (!role || user.role === role)
),
term,
role
}))
);
Flattening Operators
Operator Behavior Best use Avoid when
switchMapCancels previous inner Observable and switches to latest. Search, route param HTTP calls, latest result wins. Every request must finish.
mergeMapRuns inner Observables concurrently. Independent writes, parallel requests, event fan-out. Order matters or concurrency must be limited.
concatMapQueues inner Observables and runs one at a time. Ordered saves, sequential jobs, queue behavior. Latest request should cancel old one.
exhaustMapIgnores new source values while inner Observable is active. Login, payment, submit button duplicate prevention. User should be able to cancel and start a newer request.
Submit button with exhaustMap
Copy
save-form.component.ts
saveClicks$ = fromEvent(saveButton, 'click').pipe(
exhaustMap(() => this.api.saveForm(this.form.value).pipe(
catchError(error => {
this.toast.error('Save failed');
return EMPTY;
})
))
);
Utility and Side-Effect Operators
Operator Explanation Example use
tapRuns side effects without changing value. Logging, analytics, debugging.
delayDelays emissions. Demo loading, backoff sketch.
timeoutErrors if source takes too long. Fail slow request with fallback.
startWithEmits an initial value before source values. Initial loading or default UI state.
endWithEmits value after source completes. Completion marker.
defaultIfEmptyEmits fallback if source completes without value. No result fallback.
isEmptyEmits whether source had no values. Empty-state detection.
Error Handling Operators
Operator Explanation Important rule
catchErrorCatches an error and returns a replacement Observable. Always return an Observable.
retryRetries failed source a fixed number of times. Use only when retry is safe.
retryWhenCustom retry strategy using another stream. Useful for backoff logic.
finalizeRuns cleanup on complete, error, or unsubscribe. Good for loading flags.
HTTP error handling
Copy
users.service.ts
users$ = this.http.get<User[]>('/api/users').pipe(
retry({ count: 2, delay: 500 }),
catchError(error => {
this.logger.error(error);
return of([]);
}),
finalize(() => this.loading.set(false))
);
Multicasting and Caching
Tool Explanation Use carefully
SubjectManual multicast source. Can become global mutable state.
BehaviorSubjectStores latest value and emits it to new subscribers. Good for current state.
ReplaySubjectReplays previous values. Always bound buffer size/time if possible.
shareShares one subscription while subscribers exist. Good for avoiding duplicated side effects.
shareReplayShares subscription and replays cached values. Use correct reset/refCount behavior to avoid stale cache.
Cache one HTTP result
Copy
profile.service.ts
profile$ = this.http.get<Profile>('/api/profile').pipe(
shareReplay({ bufferSize: 1, refCount: true })
);
Angular Operator Patterns
Search field: map -> debounceTime -> distinctUntilChanged -> switchMap.
Route data: paramMap -> map -> switchMap.
Save button: click stream -> exhaustMap.
Ordered autosave: form value stream -> concatMap.
Dashboard view model: combineLatest of data, filters, and selected state.
Manual subscription cleanup: takeUntilDestroyed or framework lifecycle helper.
Template rendering: prefer async pipe instead of manual subscribe when possible.
Common Operator Mistakes
Using mergeMap for search, causing older responses to overwrite newer results.
Using switchMap for writes that must all complete.
Putting catchError at the end when only an inner request should recover.
Forgetting to return an Observable from catchError.
Using unbounded ReplaySubject and retaining too much memory.
Creating duplicate HTTP requests because a cold Observable is subscribed multiple times.
Hiding business logic in tap instead of keeping it explicit.
Manually subscribing in Angular when the async pipe would handle lifecycle.
25 Most Important Practice Questions
1. Which operator transforms each value? Answer: map.
2. Which operator filters values by condition? Answer: filter.
3. Which operator waits for typing to stop? Answer: debounceTime.
4. Which operator skips repeated consecutive values? Answer: distinctUntilChanged.
5. Which operator cancels old HTTP requests in search? Answer: switchMap.
6. Which operator runs inner streams concurrently? Answer: mergeMap.
7. Which operator queues async work in order? Answer: concatMap.
8. Which operator ignores duplicate submits while one request is active? Answer: exhaustMap.
9. Which operator combines latest values from multiple streams? Answer: combineLatest.
10. Which operator waits for multiple HTTP requests to complete once? Answer: forkJoin.
11. Which operator samples latest state when a button is clicked? Answer: withLatestFrom.
12. Which operator runs side effects without changing values? Answer: tap.
13. Which operator catches errors? Answer: catchError.
14. What must catchError return? Answer: An Observable, such as of([]) or throwError().
15. Which operator runs cleanup on complete, error, or unsubscribe? Answer: finalize.
16. Which operator retries failed source? Answer: retry.
17. Which operator provides custom retry strategy? Answer: retryWhen.
18. Which operator emits an initial value? Answer: startWith.
19. Which operator accumulates state over time? Answer: scan.
20. Which operator compares previous and current value? Answer: pairwise.
21. Which operator batches values by time? Answer: bufferTime.
22. Which operator shares one subscription? Answer: share.
23. Which operator shares and replays cached values? Answer: shareReplay.
24. Which creation operator listens to DOM events? Answer: fromEvent.
25. Which creation operator emits values every interval? Answer: interval.