1. Introduction to RxJS

What is RxJS?

RxJS stands for Reactive Extensions for JavaScript. It's a library for reactive programming using Observables that makes it easier to compose asynchronous or callback-based code.

💡 Key Insight: RxJS transforms callbacks and promises into composable Observable streams that can be easily combined, filtered, and transformed.

Core Concepts

1. Observable

A lazy push collection of multiple values over time. It's like a JavaScript generator but for async operations.

const observable$ = new Observable(subscriber => { subscriber.next(1); subscriber.next(2); subscriber.next(3); subscriber.complete(); });
2. Observer

An object with three callback functions: next, error, and complete.

const observer = { next: (value) => console.log('Value:', value), error: (err) => console.error('Error:', err), complete: () => console.log('Done!') };
3. Subscription

The connection between an Observer and an Observable. Always unsubscribe to avoid memory leaks!

const subscription = observable$.subscribe(observer); // Later, unsubscribe to prevent memory leaks subscription.unsubscribe();
4. Operator

A pure function that takes an Observable and returns an Observable. Operators are the foundation of reactive programming.

observable$.pipe( map(x => x * 2), // Transform each value filter(x => x > 5) // Only pass values > 5 ).subscribe(observer);

Why RxJS in Angular?

  • Composable: Easily combine multiple async operations
  • Memory Safe: Built-in subscription management
  • Powerful: 100+ operators for any transformation
  • Angular Native: HttpClient, FormControl, and Router all return Observables
  • Performance: Lazy evaluation and unsubscription prevent memory leaks

Installation & Setup

NPM Installation
npm install rxjs
Import in Angular
import { Observable, of } from 'rxjs'; import { map, filter } from 'rxjs/operators';
Standalone API Import (Angular 15+)
import { Component, OnInit, OnDestroy } from '@angular/core'; import { Subject } from 'rxjs'; @Component({ selector: 'app-example', template: '
{{ message }}
' }) export class ExampleComponent implements OnInit, OnDestroy { private destroy$ = new Subject(); ngOnDestroy() { this.destroy$.next(); this.destroy$.complete(); } }

2. Core Concepts Deep Dive

Creating Observables

of()

Emits a fixed set of values synchronously

of(1, 2, 3).subscribe(x => console.log(x));

from()

Converts array/promise/iterable to Observable

from([1, 2, 3]).subscribe(x => console.log(x));

range()

Emits sequential numbers within range

range(1, 5).subscribe(x => console.log(x));

interval()

Emits sequential numbers at intervals (ms)

interval(1000).subscribe(x => console.log(x));

timer()

Emits after delay, then optionally at interval

timer(2000, 1000).subscribe(x => console.log(x));

create()

Custom Observable with full control

new Observable(sub => sub.next(42));

3. Essential Operators

Higher-Order Operators Comparison

Operator Cancels Previous Order Use Case
switchMap() ✅ Yes Latest Search, routing, latest value matters
mergeMap() ❌ No All Concurrent Parallel requests, all matter
concatMap() ❌ No Sequential Preserve order, sequential operations
exhaustMap() ✅ Yes Current Ignore while processing, button clicks

4. Subjects & Multicasting

What is a Subject?

A Subject is both an Observer and an Observable. It allows multicasting - emitting to multiple Subscribers from a single source.

💡 Key Difference: Observable is cold (each subscription creates new execution), Subject is hot (all subscribers share one execution).

Real-World Example: Sharing Data Between Components

// shared.service.ts 
import {
    Injectable
} from '@angular/core';
import {
    BehaviorSubject
} from 'rxjs';
@Injectable({
    providedIn: 'root'
}) export class SharedService {
    private messageSource = new BehaviorSubject('Hello');
    message$ = this.messageSource.asObservable();
    updateMessage(msg: string) {
        this.messageSource.next(msg);
    }
}
// component-a.ts 
constructor(private shared: SharedService) {}
sendMessage() {
    this.shared.updateMessage('Message from A');
}
// component-b.ts 
constructor(private shared: SharedService) {}
ngOnInit() {
    this.shared.message$.subscribe(msg => {
        console.log('Received:', msg);
    });
}

5. Real-World Use Cases

Debounced Search in Angular

Implement a search that waits for user to stop typing before making API call:

// search.component.ts 
import {
    Component
} from '@angular/core';
import {
    FormControl
} from '@angular/forms';
import {
    debounceTime,
    distinctUntilChanged,
    switchMap
} from 'rxjs/operators';
@Component({
        selector: 'app-search',
        template: ` <input [formControl]="searchCtrl" placeholder="Search users..."> <div *ngFor="let user of results$ | async">{{ user.name }}</div> `
    }) export class SearchComponent {
        searchCtrl = new FormControl('');
        results$ = this.searchCtrl.valueChanges.pipe(debounceTime(300),
                // Wait 300ms after last keystroke distinctUntilChanged(), 
                // Skip if value unchanged switchMap(query => // Cancel previous, fetch new this.api.searchUsers(query) ) ); constructor(private api: ApiService) {} }

API Polling with Interval

Periodically fetch data and update view automatically:

// polling.component.ts import { interval } from 'rxjs'; import { switchMap, startWith } from 'rxjs/operators'; export class PollingComponent { data$ = interval(5000).pipe( startWith(0), // Start immediately switchMap(() => // Fetch every 5 seconds this.api.getData() ) ); constructor(private api: ApiService) {} }

Cancel HTTP Requests

Abort pending requests when component destroys:

// user-detail.component.ts import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; @Component({...}) export class UserDetailComponent implements OnInit, OnDestroy { private destroy$ = new Subject<void>(); user$ = this.route.params.pipe( takeUntil(this.destroy$), // Unsubscribe when destroy$ emits switchMap(params => this.api.getUser(params.id) ) ); ngOnDestroy() { this.destroy$.next(); // Signal to unsubscribe this.destroy$.complete(); } constructor( private api: ApiService, private route: ActivatedRoute ) {} }

Combining Multiple API Calls

Wait for multiple async operations and combine results:

// dashboard.component.ts import { combineLatest } from 'rxjs'; import { map } from 'rxjs/operators'; @Component({...}) export class DashboardComponent { dashboard$ = combineLatest([ this.api.getUser(), this.api.getStats(), this.api.getNotifications() ]).pipe( map(([user, stats, notifications]) => ({ user, stats, notifications })) ); constructor(private api: ApiService) {} }

Handle HTTP Errors with Retry

Automatically retry failed requests with exponential backoff:

// api.service.ts import { retry, retryWhen, delay } from 'rxjs/operators'; import { throwError } from 'rxjs'; fetchWithRetry(url: string) { return this.http.get(url).pipe( retryWhen(errors => errors.pipe( delay(1000), // Wait 1s before retry take(3), // Max 3 retries throwError(() => 'Max retries exceeded') ) ), catchError(error => { console.error('Failed:', error); return throwError(() => error); }) ); }

6. RxJS Interview Questions

30+ Interview Questions & Answers

1.What is RxJS?

RxJS stands for Reactive Extensions for JavaScript — a library for working with asynchronous data streams like events, API responses, timers, etc.

📌 Think of a water pipe (stream) where data flows continuously and you can react to it anytime.

Use Cases:

  • Search input autocomplete
  • Live notifications
  • Real time stock price update

2.What is an Observable?

An Observable is a data producer — it emits values over time.

Real-life analogy:

You subscribe to a YouTube channel → new video notifications keep coming.

Example:

const obs$ = new Observable(sub => {
  sub.next('Hi!');
  sub.next('Hello!');
  sub.complete();
});

3.What is an Observer?

An Observer is a consumer — it listens to data from Observable.

obs$.subscribe({
  next: value => console.log(value),
  error: err => console.log(err),
  complete: () => console.log('done!')
});

4.What is Subscription?

A subscription connects an observer to an observable.

Like:

Gym membership → you receive facilities (data).
You unsubscribe → no more facilities.

const sub = obs$.subscribe();
sub.unsubscribe();

5.What is a Subject?

A Subject is both Observable + Observer.
It allows multicasting — emits to multiple subscribers.

Real life:

WhatsApp group message → everyone gets it.

const sub$ = new Subject();
sub$.subscribe(v => console.log('A:', v));
sub$.subscribe(v => console.log('B:', v));
sub$.next('Hi');

6.Subject vs BehaviorSubject?

FeatureSubjectBehaviorSubject
Initial value❌ No✔️ Yes
Last value replay❌ No✔️ Yes

Use case:

  • Store logged-in user state and let components auto-update.
const user$ = new BehaviorSubject(null);
user$.next({name:'Jignesh'});

7.ReplaySubject vs AsyncSubject?

TypeBehavior
ReplaySubjectReplays N previous values
AsyncSubjectEmits only last value after complete

Use case:
Replay last 3 chat messages when new user joins.


8.What are Operators in RxJS?

Operators are functions that transform, filter, or combine streams.

Types:

  • Creation → of(), from(), interval()
  • Transformation → map(), switchMap()
  • Filtering → filter(), debounceTime()
  • Combination → merge(), combineLatest()
  • Utility → tap(), finalize()

9.Cold vs Hot Observable?

ColdHot
Starts emitting when subscribedKeeps emitting
API callsWebSocket, Subject

Example:

Watching a recorded movie vs live IPL match 🎯


10.What is Pipeable Operator?

Operators used via .pipe() for clean chaining.

obs$.pipe(
  filter(v => v > 5),
  map(v => v * 2)
);

11.What is map() in RxJS?

Transforms each emitted value.

of(1,2,3).pipe(
  map(v => v * 10)
);

Use case → Format API response


12.What is switchMap()?

Cancels previous request and subscribes to new one.

Best for:

  • Live search — Fast typing cancels old requests
searchInput$.pipe(
  switchMap(term => this.api.search(term))
);

13.switchMap vs mergeMap vs concatMap vs exhaustMap

OperatorBehaviorBest Use
switchMapCancel old, take newSearch input
mergeMapRun parallel requestsChat uploads
concatMapQueue (1 by 1)Form submit logs
exhaustMapIgnore new until current completesPrevent double click on Pay button

Fantastic interview question.


14.What is debounceTime()?

Waits for user to stop typing before sending request.

keyup$.pipe(debounceTime(500))

Use case → search, autocomplete


15.What is throttleTime()?

Allows value only every X time.

Use case → Prevent rapid button clicks


16.What is take() / takeUntil()?

Unsubscribe automatically after specific condition.

takeUntil(this.destroy$)

Use case → Prevent memory leaks in Angular


17.What is tap()?

Side-effect operator — used for logging, debugging.

tap(v => console.log('Value:', v))

18.What is shareReplay()?

Shares the same API result with future subscribers.

Use case:

  • Avoid multiple API calls → Cache response

19.combineLatest vs forkJoin

OperatorWhen values emitted?
combineLatestWhenever any observable emits
forkJoinWhen all complete (like Promise.all)

Use case:

  • combineLatest → calculate total cart price live
  • forkJoin → load page after all API calls finish

20.What is error handling in RxJS?

Use catchError() and optionally retry().

api$.pipe(
  retry(2),
  catchError(err => of([]))
);

21.What is a Scheduler?

Controls when observable delivers values
(e.g., asyncScheduler → delay in microtasks).


22.What is finalize()?

Runs when observable completes or unsubscribes — cleanup.

finalize(() => loader.hide())

23.What is distinctUntilChanged()?

Prevent duplicate API calls & UI renders.


24.Multicasting in RxJS?

One execution shared with multiple subscribers → using Subject / share()


25.Why RxJS in Angular?

✔ HTTP returns Observable
✔ Forms & events
✔ Router events
✔ State management (NgRx)


26.What is backpressure in RxJS?

Handling too many values emitted too fast
Use → throttleTime(), sampleTime()


27.What is defer()?

Creates observable only when subscribed
Useful for lazy-loaded API calls


28.What is timer() & interval()?

Create streams over time:

timer(2000), interval(1000)

Use case → Auto logout timer


29.How to cancel previous subscription automatically?

Use takeUntil()

this.sub$.pipe(takeUntil(this.destroy$))

30.Most used RxJS operator in Angular HTTP?

switchMap() + catchError() + tap() + shareReplay()


31.How to Convert Promise to Observable?

from(fetch('/api/users'))

7. Advanced Topics

Cold vs Hot Observables

Feature Cold Observable Hot Observable
Execution Starts on subscription Exists before subscription
Each Subscriber Gets own execution Shares single execution
Example of(), from(), interval() Subject, click events
Data Loss No loss Yes, before subscription
Making Cold Observable Hot with shareReplay()
// Cold: Each subscriber triggers HTTP call const data$ = this.http.get('/api/data'); // Hot: First subscriber triggers, others share result const data$ = this.http.get('/api/data').pipe( shareReplay(1) // Cache 1 last value );

Memory Leaks & Subscription Management

❌ WRONG - Memory Leak:
export class BadComponent implements OnInit {
    ngOnInit() {
            this.api.getData().subscribe(data => {
                        // No unsubscribe! Memory leak if component destroyed }); } }
✅ CORRECT - Using takeUntil:
export class GoodComponent implements OnInit, OnDestroy {
    private destroy$ = new Subject < void > ();
    ngOnInit() {
            this.api.getData().pipe(takeUntil(this.destroy$)
                    // Unsubscribe when destroy$ emits ).subscribe(data => { console.log(data); }); } ngOnDestroy() { this.destroy$.next(); this.destroy$.complete(); } }
✅ ALSO CORRECT - Using async pipe (Best in templates):

// Component export class Component { data$ = this.api.getData(); } // Template - async pipe handles unsubscribe automatically <div>{{ data$ | async | json }}</div>

Custom Operators

Create reusable custom operators for your business logic:

// Custom operator: double values 
import {
    pipe
} from 'rxjs';
import {
    map
} from 'rxjs/operators';
const double = () => pipe(map(x => x * 2));
// Usage of(1, 2, 3).pipe(double()).subscribe(console.log); // 2, 4, 6 
// Advanced: Multi-step custom operator const asyncOp = (delayMs) => pipe( delay(delayMs), tap(x => console.log(`Emitted after ${delayMs}ms`)), catchError(err => { console.error('Error:', err); return of(null); }) );

Performance Optimization Checklist

✅ DO:

  • Use takeUntil() to unsubscribe automatically
  • Use async pipe in templates
  • Use shareReplay() for shared subscriptions
  • Use switchMap() for latest-only scenarios
  • Implement proper error handling with catchError()

❌ DON'T:

  • Create nested subscriptions
  • Forget to unsubscribe in ngOnDestroy
  • Use mergeMap() for unlimited concurrent requests
  • Subscribe multiple times to the same Observable
  • Ignore memory leaks - use Angular DevTools to detect

💡 Tips & Tricks for Interview Success

📌 Remember:

  • Observable vs Promise: Observable is lazy and cancellable, Promise is eager and not cancellable
  • switchMap vs mergeMap: switchMap = cancel previous, mergeMap = all concurrent
  • Subject gotcha: Subscribers miss values emitted before they subscribe
  • Marble diagrams: X = complete, | = error, - = time, values = emissions
  • Pipe operator: Composes operators from left to right, each transforms the stream
  • Unsubscribe patterns: Always clean up: takeUntil, async pipe, or manual unsubscribe
  • Cold vs Hot: Cold = per subscriber, Hot = shared (use shareReplay to convert)
  • Error propagation: Error stops the stream! Use catchError to recover