Observables in Angular
Observables in Angular
In Angular, Observables are a foundational
mechanism for managing asynchronous data streams and event handling. They offer
a powerful, flexible way to react to changing data over time.
Key Features of Observables
- Unlike
Promises that emit a single value, Observables can emit multiple values
over time — ideal for continuous streams like user input, live data, or
WebSockets.
- Observables
are lazy — they only start emitting values when explicitly subscribed to.
This improves performance and avoids unnecessary computation.
- You can
unsubscribe from an Observable using .unsubscribe() to clean up resources
and prevent memory leaks.
- Observables
leverage the RxJS library, which provides a rich set of
operators like: map, filter, debounceTime, mergeMap, etc. These enable
powerful, declarative transformations and compositions of data streams.
- Observables
are deeply embedded in Angular’s architecture and power many core
features:
- HttpClient
- Reactive
Forms
- Router
events
- @Output
using EventEmitter
- WebSocket
communication
- Observables
streamline handling of next, error, and complete notifications within a
single, cohesive API — making error handling and stream finalization more
structured.
Simply:
· Emit
multiple values over time
· Lazy
execution (runs only when subscribed)
· Cancelable
via unsubscribe()
· Rich
with RxJS operators
· Handles
next, error, and complete
· Core
to Angular's async architecture
When you subscribe to an Observable, you provide up
to three callbacks:
next(value)
- Purpose: Handles each emitted
value from the Observable.
- Use Case: Called every time the
Observable produces a value.
- Example: Receiving user data, form input
changes, HTTP success responses.
error(err)
- Purpose: Called once when
an error occurs in the Observable stream.
- Use Case: Handles issues like HTTP
failures, runtime exceptions, or logic errors.
- Effect: Terminates the Observable—no
more values will be emitted after this.
complete()
- Purpose: Called once when
the Observable has finished emitting all values and is
done.
- Use Case: Useful for triggering cleanup
or final actions once the stream is complete.
- Important: Unlike error(), complete()
indicates successful completion, not failure.
Summary Table
Method |
Called When |
Stream Ends? |
Example Use Case |
next() |
A new value is emitted |
❌ No |
New chat message, HTTP data, etc. |
error() |
An error occurs |
✅ Yes |
HTTP 404/500, network issue |
complete() |
Stream ends normally (no errors) |
✅ Yes |
File upload complete, HTTP done |
Best Practices
- Use
unsubscribe() or takeUntil() to manage subscriptions.
- Prefer the
async pipe in templates for automatic subscription management.
- Avoid
unnecessary subscriptions—compose, don't nest Observables.
Basic Example: Observable Emitting Values Over Time
import { Observable } from 'rxjs';
const obs = new Observable(observer => {
observer.next("First value");
setTimeout(() => observer.next("Second
value"), 1000);
setTimeout(() => observer.complete(), 2000);
});
obs.subscribe({
next: val => console.log(val),
complete: () => console.log("Completed")
});
Output in Console
First value
Second value
Completed
Extended Example with Unsubscribe and Error Handling
import { Observable, Subscription } from 'rxjs';
const obs = new Observable<string>(observer => {
observer.next("First value");
const second = setTimeout(() =>
observer.next("Second value"), 1000);
const complete = setTimeout(() => observer.complete(),
2000);
// Optional error simulation:
// const error = setTimeout(() =>
observer.error("Something went wrong"), 1500);
return () => {
clearTimeout(second);
clearTimeout(complete);
console.log("Unsubscribed and cleaned
up");
};
});
const subscription: Subscription = obs.subscribe({
next: val => console.log("Next:", val),
error: err => console.error("Error:",
err),
complete: () => console.log("Completed")
});
setTimeout(() => {
subscription.unsubscribe();
}, 1500);
Output in Console
Next: First value
Next: Second value
Unsubscribed and cleaned up
Angular Service and Component Example
data.service.ts
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class DataService {
getData(): Observable<string> {
return new Observable(observer => {
observer.next("First
value");
const t1 = setTimeout(() =>
observer.next("Second value"), 1000);
const t2 = setTimeout(() =>
observer.complete(), 2000);
return () => {
clearTimeout(t1);
clearTimeout(t2);
console.log("Observable
cleaned up");
};
});
}
}
app.component.ts
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs';
import { DataService } from './data.service';
@Component({
selector: 'app-root',
template: `<p>Check console for
output</p>`
})
export class AppComponent implements OnInit, OnDestroy {
private subscription!: Subscription;
constructor(private dataService: DataService) {}
ngOnInit() {
this.subscription =
this.dataService.getData().subscribe({
next: val =>
console.log("Received:", val),
error: err =>
console.error("Error:", err),
complete: () =>
console.log("Stream completed")
});
setTimeout(() => {
this.subscription.unsubscribe();
}, 1500);
}
ngOnDestroy() {
if (this.subscription) {
this.subscription.unsubscribe();
}
}
}
Why Use Observables in Angular?
Observables, when combined with RxJS operators, provide a reactive,
elegant, and maintainable programming model. They help you build
more scalable and readable Angular applications by handling
async workflows with precision and declarative logic.
Comments
Post a Comment