Creating and Subscribing to Observables
An Observable is a stream that emits zero or more values over time, then completes or errors.
Creating an Observable
import { Observable } from 'rxjs';
const numbers$ = new Observable<number>(subscriber => {
subscriber.next(1);
subscriber.next(2);
subscriber.next(3);
subscriber.complete();
});
Subscribing
const subscription = numbers$.subscribe({
next: value => console.log('Value:', value),
error: err => console.error('Error:', err),
complete: () => console.log('Complete'),
});
// Output:
// Value: 1
// Value: 2
// Value: 3
// Complete
Unsubscribing
const subscription = numbers$.subscribe(value => console.log(value));
// Later, to stop receiving values
subscription.unsubscribe();
Cold vs Hot Observables
| Type | Behavior | Example |
|---|---|---|
| Cold | Each subscriber gets its own independent stream | HttpClient, of(), from() |
| Hot | All subscribers share the same stream | Subjects, DOM events, WebSockets |
// Cold — each subscriber triggers the producer
const cold$ = new Observable(subscriber => {
console.log('Producer executed');
subscriber.next(Math.random());
});
cold$.subscribe(v => console.log('A:', v)); // Producer executed, A: 0.123
cold$.subscribe(v => console.log('B:', v)); // Producer executed, B: 0.456
// Hot — producer runs once, all subscribers share
const hot$ = new Subject<number>();
hot$.subscribe(v => console.log('A:', v));
hot$.subscribe(v => console.log('B:', v));
hot$.next(42); // A: 42, B: 42 (same value)
Cleanup and Memory Leak Prevention
Subscriptions must be cleaned up to prevent memory leaks. Angular provides several mechanisms.
Manual Unsubscribe
@Component({
selector: 'app-example',
template: `...`,
})
export class ExampleComponent implements OnDestroy {
private destroy$ = new Subject<void>();
ngOnInit(): void {
this.dataService.getData().pipe(
takeUntil(this.destroy$)
).subscribe(data => {
this.data = data;
});
}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
}
takeUntil Operator
import { takeUntil } from 'rxjs/operators';
this.dataService.getData().pipe(
takeUntil(this.destroy$)
).subscribe();
async Pipe (Automatic Cleanup)
<div *ngIf="data$ | async as data">
{{ data }}
</div>
The async pipe automatically unsubscribes when the component is destroyed.
take(1) — Auto-Complete After First Emission
this.route.paramMap.pipe(
take(1)
).subscribe(params => {
// Only runs once
});
Common Memory Leak Patterns
// BAD: No cleanup
ngOnInit(): void {
this.dataService.getData().subscribe(data => {
this.data = data;
});
}
// GOOD: takeUntil pattern
ngOnInit(): void {
this.dataService.getData().pipe(
takeUntil(this.destroy$)
).subscribe(data => {
this.data = data;
});
}
// GOOD: async pipe in template
data$ = this.dataService.getData();
Quiz
1. What is the difference between a cold and hot Observable?
2. How do you prevent memory leaks from subscriptions?
3. What does takeUntil(destroy$) do?
Flashcards
Question
Cold vs Hot Observable?
Click to reveal answer
Answer
Cold: per-subscriber stream (HttpClient). Hot: shared stream (Subjects).
Question
How to prevent memory leaks?
Click to reveal answer
Answer
Unsubscribe in ngOnDestroy, use takeUntil, or use async pipe.
Question
What does takeUntil do?
Click to reveal answer
Answer
Completes the Observable when the notifier emits, auto-unsubscribing.
Revision Notes
Key Takeaways
- 1. Cold Observables run per subscriber; hot Observables share
- 2. Always unsubscribe to prevent memory leaks
- 3. takeUntil(destroy$) is the standard cleanup pattern
- 4. async pipe handles subscription and cleanup automatically
Interview Tips
- • Explain cold vs hot Observables
- • Be ready to describe memory leak prevention
- • Mention the async pipe as the preferred template pattern
Cheat Sheet
Observables and Subscriptions Cheat Sheet
Create:
new Observable(subscriber => {
subscriber.next(value);
subscriber.complete();
})
Subscribe:
const sub = observable$.subscribe({
next: v => {},
error: e => {},
complete: () => {},
});
sub.unsubscribe();
Cleanup patterns:
- takeUntil(destroy$) in ngOnDestroy
- async pipe in template
- take(1) for one-time reads