This practical guide covers the essential operators and patterns in RxJS for effective reactive programming in JavaScript. We'll explore everything from basic Observable creation to advanced patterns, with runnable examples for each concept.
$ npm install rxjs added 2 packages in 2s 28 packages are looking for funding run `npm fund` for details
RxJS version loaded'}
Let's start with the fundamentals of RxJS: Observables, Observers, and Subscriptions.
Basic Observable example: Received: First value Received: Second value Observable completed
Async Observable example: Received: First value
Creation operators let you create Observables from various sources.
of operator example: of emitted: 1 of emitted: 2 of emitted: 3 of emitted: 4 of emitted: 5
from operator with array example: from array emitted: 10 from array emitted: 20 from array emitted: 30 from operator with promise example: from promise emitted: Resolved value
interval and timer example: interval: 0 interval: 1 timer: 0 interval: 2 timer: 1 interval: 3 timer: 2 interval: 4 Interval and timer demo completed
range operator example: range emitted: 5 range emitted: 6 range emitted: 7 range emitted: 8 range emitted: 9
fromEvent example (simulated for Node.js): Click event: {"type":"click","target":"button1"} Click event: {"type":"click","target":"button2"}
These operators transform the items emitted by an Observable.
map operator example: Squared: 1 Squared: 4 Squared: 9 Squared: 16 Squared: 25
property extraction example: Name: Alice Name: Bob Name: Charlie
scan operator example: Total clicks: 1 Total clicks: 2 Total clicks: 3 Total clicks: 4 Total clicks: 5
mergeMap operator example: Fetching user 1 Fetching user 2 Fetching user 3 Got user: {"id":1,"name":"User 1"} Got user: {"id":2,"name":"User 2"} Got user: {"id":3,"name":"User 3"} mergeMap completed
switchMap example: Searching for: react Searching for: reactive Searching for: rxjs Results for rxjs switchMap completed
These operators filter the values emitted by an Observable.
filter operator example: Even number: 2 Even number: 4 Even number: 6 Even number: 8 Even number: 10
take operator example: Taken value: 1 Taken value: 2 Taken value: 3 Completed after taking 3 values
takeUntil operator example: Taking values for 1 second... Value: 0 Value: 1 Value: 2 Value: 3 Completed after notifier fired
skip operator example: After skip: 6 After skip: 7 After skip: 8 After skip: 9 After skip: 10
debounceTime operator example: Simulating typing with debounce... Typing: h Typing: he Typing: hel Typing: hell Debounced value: hell Typing: hello Debounced value: hello Debounce demo completed
distinctUntilChanged operator example: Distinct value: 1 Distinct value: 2 Distinct value: 1 Distinct value: 3
These operators combine multiple Observables.
merge operator example: First: 0 Second: 0 First: 1 Second: 1 First: 2 Merge completed
concat operator example: Concatenated: A Concatenated: B Concatenated: C Concatenated: X Concatenated: Y Concatenated: Z
combineLatest operator example: Combined: 0, 0 Combined: 1, 0 Combined: 2, 0 Combined: 2, 1 Combined: 2, 2 combineLatest completed
zip operator example: Zipped: A1 Zipped: B2 Zipped: C3
forkJoin operator example: Waiting for all APIs to respond... All responses: ["API 1 Response","API 2 Response","API 3 Response"] forkJoin completed
These operators help manage errors in your Observable streams.
catchError operator example: Value: 1 Value: 2 Value: 3 Error caught: Something went wrong! Value: Fallback value Completed
retry operator example: Attempt 1 Attempt 2 Attempt 3 Value: Success! Completed after retries
Let's explore some common patterns and recipes using RxJS.
Typeahead search pattern: User typed: r User typed: rx User typed: rxj API search for: rxj User typed: rxjs API search for: rxjs Search results: 4 results for "rxjs" Typeahead demo completed
Caching pattern example: First request: Making new API request Received: {"data":"Fresh data","timestamp":"2025-03-14T12:19:31.310Z"} Second request (after 300ms): Returning cached data Received: {"data":"Fresh data","timestamp":"2025-03-14T12:19:31.310Z","fromCache":true} Third request (after cache expiry): Making new API request Received: {"data":"Fresh data","timestamp":"2025-03-14T12:19:33.116Z"}
Retry with backoff pattern: API attempt 1 Retry attempt 1 after error: Server error on attempt 1 Waiting 300ms before retry 1 API attempt 2 Retry attempt 2 after error: Server error on attempt 2 Waiting 600ms before retry 2 API attempt 3 Success! {"success":true,"data":"API data payload"} Retry pattern demo completed
RxJS provides tools for testing Observables with marble diagrams and virtual time scheduling.
Actual: [{"frame":5,"notification":{"kind":"N","value":20}},{"frame":8,"notification":{"kind":"N","value":30}},{"frame":11,"notification":{"kind":"C"}}] Expected: [{"frame":5,"notification":{"kind":"N","value":20}},{"frame":8,"notification":{"kind":"N","value":30}},{"frame":11,"notification":{"kind":"C"}}] Equal: true Marble testing demo complete
Let's solve some practical problems with RxJS.
API rate limiter implementation: Queueing request for ID 1 Queueing request for ID 2 Queueing request for ID 3 Queueing request for ID 4 Queueing request for ID 5 API call for ID 1 started API call for ID 2 started Queueing request for ID 6 Queueing request for ID 7 Result received: Result for ID 1 API call for ID 3 started Result received: Result for ID 2 API call for ID 4 started Result received: Result for ID 3 API call for ID 5 started Result received: Result for ID 4 API call for ID 6 started Result received: Result for ID 5 API call for ID 7 started Result received: Result for ID 6 Result received: Result for ID 7
Here are some tips for optimizing RxJS performance:
shareReplay
- use {refCount: true}
config to avoid memory leaksshare()
for multicasting when multiple subscribers need the same datatake(1)
for one-time operations which automatically completesSubject
for event buses which is more efficient than multiple ObservablesswitchMap
for scenarios where you only care about the latest value (like search)mergeMap
when you need all results quickly but don't care about orderconcatMap
when order is importantexhaustMap
when you want to ignore new events during processingThis notebook has covered the essential operators and patterns in RxJS for reactive programming. We've explored synchronous and asynchronous Observable creation, transformation, filtering, combination, error handling, and practical real-world patterns.
Reactive programming with RxJS gives you powerful tools to handle complex async operations and data flows in a declarative way. While it has a learning curve, the patterns we've covered will help you write more maintainable and predictable asynchronous code.