Observable
Think of an Observable as a stream of data or events that happens over time. It's like subscribing to a newsletter or a YouTube channel. Once you subscribe, you start receiving updates (emails, new videos) whenever they are published. You can receive zero, one, or many updates over the lifetime of that subscription.
Key Ideas:#
- Stream of Values: Unlike a function that returns a single value, or a Promise that resolves with a single value (or rejects), an Observable can emit multiple values over time.
- Lazy: Observables are "lazy." They don't start emitting values until someone actually subscribes to them. If nobody subscribes, the code that produces the values won't even run.
- Asynchronous (or Synchronous): They are often used for asynchronous operations (like fetching data from a server), but they can also emit values synchronously.
- Cancellation: You can "unsubscribe" from an Observable, meaning you stop listening to the stream. This is important for cleaning up resources and preventing memory leaks.
Real-World Analogy:#
Imagine you've ordered food online.
- You place the order (this is like creating the Observable).
- The system doesn't start processing your order until you actually confirm it (like subscribing).
- Then, you get updates over time:
- "Order received" (first value)
- "Restaurant is preparing your food" (second value)
- "Delivery partner assigned" (third value)
- "Food is on the way" (fourth value)
- "Delivered" (fifth value, and maybe the stream *completes here*)
- If something goes wrong (e.g., restaurant cancels), you get an error notification (error).
- You can choose to stop receiving notifications if you want (unsubscribe).
How it's used in Angular (Examples):#
-
Fetching Data with
HttpClient
: This is the most common use case. When you make an HTTP request using Angular'sHttpClient
, it returns an Observable. You subscribe to this Observable to get the response from the server.Service (
data.service.ts
):#import { Injectable, inject } from "@angular/core"; import { HttpClient } from "@angular/common/http"; import { Observable } from "rxjs"; @Injectable({ providedIn: "root", }) export class DataService { private readonly http = inject(HttpClient); private readonly apiUrl = "https://api.example.com/items"; getItems(): Observable<any[]> { return this.http.get<any[]>(this.apiUrl); } }
Component (
my-component.component.ts
):#import { Component, inject, effect } from "@angular/core"; import { DataService } from "./data.service"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; @Component({ selector: "app-my-component", template: ` <ul> <li *ngFor="let item of items">{{ item.name }}</li> </ul> <div *ngIf="errorMsg">{{ errorMsg }}</div> `, }) export class MyComponentComponent { private readonly dataService = inject(DataService); items: any[] = []; errorMsg = ""; constructor() { this.dataService .getItems() .pipe(takeUntilDestroyed()) .subscribe({ next: (data) => { this.items = data; console.log("Data received:", data); }, error: (error) => { this.errorMsg = "Failed to load items."; console.error("Error fetching items:", error); }, complete: () => { console.log("Finished fetching items."); }, }); } }
-
Reactive Forms: Listening to changes in form input values (
valueChanges
) or status (statusChanges
). - Router Events: Subscribing to events from the Angular Router to know when navigation starts, ends, or fails.
In simple terms, an Observable is a powerful way to handle sequences of events or data over time in a manageable way. You subscribe to listen, receive values/errors, and can unsubscribe when you're done.