Promises vs Observables
Promises and Observables both handle asynchronous operations, but they differ in fundamental ways that affect how you write Angular code.
Promises
A Promise represents a single future value. It is eager (starts immediately) and cannot be cancelled.
function fetchUser(id: number): Promise<User> {
return fetch(`/api/users/${id}`).then(res => res.json());
}
// Usage
fetchUser(1).then(user => console.log(user));
Observables
An Observable represents a stream of values over time. It is lazy (starts on subscribe) and can be cancelled.
import { Observable, of } from 'rxjs';
function fetchUser$(id: number): Observable<User> {
return new Observable(subscriber => {
fetch(`/api/users/${id}`)
.then(res => res.json())
.then(user => {
subscriber.next(user);
subscriber.complete();
})
.catch(err => subscriber.error(err));
});
}
// Usage — nothing happens until subscribe
fetchUser$(1).subscribe({
next: user => console.log(user),
error: err => console.error(err),
complete: () => console.log('done'),
});
Key Differences
| Feature | Promise | Observable |
|---|---|---|
| Values | Single | Multiple (stream) |
| Execution | Eager | Lazy |
| Cancellation | No | Yes (unsubscribe) |
| Operators | No | Yes (map, filter, etc.) |
| Retry | Manual | Built-in operators |
Converting Between Them
// Promise to Observable
import { from } from 'rxjs';
const observable$ = from(fetch('/api/users').then(r => r.json()));
// Observable to Promise
import { firstValueFrom } from 'rxjs';
const users = await firstValueFrom(this.http.get<User[]>('/api/users'));
async/await and the async Pipe
async/await flattens Promise chains into linear code. In Angular templates, the async pipe handles Observable subscription and cleanup automatically.
async/await with Promises
// Promise chains
loadUser(id: number): Promise<User> {
return fetch(`/api/users/${id}`)
.then(res => res.json())
.then(user => {
return fetch(`/api/users/${id}/posts`)
.then(res => res.json())
.then(posts => ({ ...user, posts }));
});
}
// async/await — same logic, linear
async loadUser(id: number): Promise<User> {
const res = await fetch(`/api/users/${id}`);
const user = await res.json();
const postsRes = await fetch(`/api/users/${id}/posts`);
const posts = await postsRes.json();
return { ...user, posts };
}
async/await with Observables
import { firstValueFrom } from 'rxjs';
async loadUsers(): Promise<User[]> {
const users$ = this.http.get<User[]>('/api/users');
return await firstValueFrom(users$);
}
The async Pipe in Templates
The async pipe subscribes to an Observable or Promise and returns the latest value. It automatically unsubscribes when the component is destroyed.
@Component({
selector: 'app-user-list',
template: `
<div *ngIf="users$ | async as users">
<p *ngFor="let user of users">{{ user.name }}</p>
</div>
<div *ngIf="loading$ | async as loading">
<p>Loading...</p>
</div>
`,
})
export class UserListComponent {
users$ = this.userService.getUsers();
loading$ = this.userService.loading$;
}
Why Angular Prefers Observables
- Cancellation — Unsubscribe cancels in-flight HTTP requests
- Composition — Operators like switchMap, mergeMap, debounceTime
- Multiple values — WebSockets, intervals, and event streams
- Lazy — Nothing runs until subscribed
Error Handling
// In component
this.userService.getUsers().subscribe({
next: users => this.users = users,
error: err => this.error = err.message,
});
// With async pipe and catchError
import { catchError, of } from 'rxjs';
users$ = this.userService.getUsers().pipe(
catchError(err => {
this.error = err.message;
return of([]);
})
);
Quiz
1. What is the key difference between a Promise and an Observable?
2. What does the async pipe do in an Angular template?
3. How do you convert an Observable to a Promise?
Flashcards
Question
Promise vs Observable — key difference?
Click to reveal answer
Answer
Promise: single value, eager, no cancellation. Observable: multiple values, lazy, cancellable, operators.
Question
What does the async pipe do?
Click to reveal answer
Answer
Subscribes to an Observable/Promise in a template and auto-unsubscribes on component destroy.
Question
How do you convert Observable to Promise?
Click to reveal answer
Answer
firstValueFrom(observable) — resolves with the first emitted value.
Revision Notes
Key Takeaways
- 1. Promises emit one value; Observables emit streams of values
- 2. Observables are lazy and cancellable; Promises are eager
- 3. The async pipe handles subscription and cleanup in templates
- 4. Angular services return Observables for composability
Interview Tips
- • Explain why Angular prefers Observables over Promises
- • Be ready to describe the async pipe's auto-unsubscribe behavior
- • Mention operators like switchMap for cancellation patterns
Cheat Sheet
Async Patterns Cheat Sheet
Promise:
fetch('/api/users').then(r => r.json()).then(users => ...)
Observable:
this.http.get<User[]>('/api/users').subscribe(users => ...)
async/await:
const users = await firstValueFrom(this.http.get<User[]>('/api/users'));
async pipe:
<div *ngIf="users$ | async as users">
{{ users | json }}
</div>