Logo
⚠️ Unsaved
[M]:

RxJS Complete Guide: All Operators and Patterns

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.

[1]:
$ npm install rxjs

added 2 packages in 2s

28 packages are looking for funding
  run `npm fund` for details
[2]:
RxJS version loaded'}
[M]:

1. Observable Basics

Let's start with the fundamentals of RxJS: Observables, Observers, and Subscriptions.

[3]:
Basic Observable example:
Received: First value
Received: Second value
Observable completed
[4]:
Async Observable example:
Received: First value
[M]:

2. Creation Operators

Creation operators let you create Observables from various sources.

[5]:
of operator example:
of emitted: 1
of emitted: 2
of emitted: 3
of emitted: 4
of emitted: 5
[6]:
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
[33]:
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
[8]:
range operator example:
range emitted: 5
range emitted: 6
range emitted: 7
range emitted: 8
range emitted: 9
[9]:
fromEvent example (simulated for Node.js):
Click event: {"type":"click","target":"button1"}
Click event: {"type":"click","target":"button2"}
[M]:

3. Transformation Operators

These operators transform the items emitted by an Observable.

[10]:
map operator example:
Squared: 1
Squared: 4
Squared: 9
Squared: 16
Squared: 25
[11]:
property extraction example:
Name: Alice
Name: Bob
Name: Charlie
[12]:
scan operator example:
Total clicks: 1
Total clicks: 2
Total clicks: 3
Total clicks: 4
Total clicks: 5
[34]:
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
[35]:
switchMap example:
Searching for: react
Searching for: reactive
Searching for: rxjs
Results for rxjs
switchMap completed
[M]:

4. Filtering Operators

These operators filter the values emitted by an Observable.

[15]:
filter operator example:
Even number: 2
Even number: 4
Even number: 6
Even number: 8
Even number: 10
[16]:
take operator example:
Taken value: 1
Taken value: 2
Taken value: 3
Completed after taking 3 values
[36]:
takeUntil operator example:
Taking values for 1 second...
Value: 0
Value: 1
Value: 2
Value: 3
Completed after notifier fired
[18]:
skip operator example:
After skip: 6
After skip: 7
After skip: 8
After skip: 9
After skip: 10
[37]:
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
[20]:
distinctUntilChanged operator example:
Distinct value: 1
Distinct value: 2
Distinct value: 1
Distinct value: 3
[M]:

5. Combination Operators

These operators combine multiple Observables.

[38]:
merge operator example:
First: 0
Second: 0
First: 1
Second: 1
First: 2
Merge completed
[22]:
concat operator example:
Concatenated: A
Concatenated: B
Concatenated: C
Concatenated: X
Concatenated: Y
Concatenated: Z
[39]:
combineLatest operator example:
Combined: 0, 0
Combined: 1, 0
Combined: 2, 0
Combined: 2, 1
Combined: 2, 2
combineLatest completed
[24]:
zip operator example:
Zipped: A1
Zipped: B2
Zipped: C3
[40]:
forkJoin operator example:
Waiting for all APIs to respond...
All responses: ["API 1 Response","API 2 Response","API 3 Response"]
forkJoin completed
[M]:

6. Error Handling

These operators help manage errors in your Observable streams.

[26]:
catchError operator example:
Value: 1
Value: 2
Value: 3
Error caught: Something went wrong!
Value: Fallback value
Completed
[27]:
retry operator example:
Attempt 1
Attempt 2
Attempt 3
Value: Success!
Completed after retries
[M]:

7. Common RxJS Patterns

Let's explore some common patterns and recipes using RxJS.

[41]:
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
[42]:
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"}
[43]:
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
[M]:

8. Testing RxJS Observables

RxJS provides tools for testing Observables with marble diagrams and virtual time scheduling.

[31]:
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
[M]:

9. Implementing Real-world Scenarios

Let's solve some practical problems with RxJS.

[44]:
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
[M]:

10. Performance Considerations and Best Practices

Here are some tips for optimizing RxJS performance:

  1. Always unsubscribe from long-lived Observables to prevent memory leaks
  2. Use pipeable operators instead of prototype operators for better tree-shaking
  3. Be careful with shareReplay - use {refCount: true} config to avoid memory leaks
  4. Use share() for multicasting when multiple subscribers need the same data
  5. Avoid excessive operators in your pipes - each adds overhead
  6. Use take(1) for one-time operations which automatically completes
  7. Consider Subject for event buses which is more efficient than multiple Observables
  8. Use appropriate flattening operators:
    • switchMap for scenarios where you only care about the latest value (like search)
    • mergeMap when you need all results quickly but don't care about order
    • concatMap when order is important
    • exhaustMap when you want to ignore new events during processing

Wrapping Up

This 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.

Sign in to save your work and access it from anywhere