Skip to content
intermediate Phase 2 · TypeScript Refresher for Angular

Async Patterns: Promises and Observables

Compare one-shot promises with multi-value observable streams.

45m
0 problems
Topic Progress 0%

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

  1. Cancellation — Unsubscribe cancels in-flight HTTP requests
  2. Composition — Operators like switchMap, mergeMap, debounceTime
  3. Multiple values — WebSockets, intervals, and event streams
  4. 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?

Question 1 options

2. What does the async pipe do in an Angular template?

Question 2 options

3. How do you convert an Observable to a Promise?

Question 3 options

Flashcards

Question

Promise vs Observable — key difference?

Answer

Promise: single value, eager, no cancellation. Observable: multiple values, lazy, cancellable, operators.

Question

What does the async pipe do?

Answer

Subscribes to an Observable/Promise in a template and auto-unsubscribes on component destroy.

Question

How do you convert Observable to Promise?

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>

Continue Learning

Before You Start

Related Reading