← Observer lesson
Lab

Observer

type Listener = (quantity: number) => void;

class Cart {
  private quantity = 0;
  private listeners = new Map<number, Listener>();
  private nextId = 1;

  getQuantity(): number {
    return this.quantity;
  }

  subscribe(listener: Listener): number {
    const id = this.nextId++;
    this.listeners.set(id, listener);
    return id;
  }

  unsubscribe(id: number): void {
    this.listeners.delete(id);
  }

  setQuantity(quantity: number): void {
    if (!Number.isInteger(quantity) || quantity < 0 || quantity > 9) return;
    if (quantity === this.quantity) return;
    this.quantity = quantity;
    // Copy the current registrations, then call each one in order.
    for (const listener of [...this.listeners.values()]) listener(quantity);
  }
}

const cart = new Cart();
let badge = cart.getQuantity();
let panel = cart.getQuantity();

cart.subscribe((quantity) => {
  badge = quantity;
});
const panelId = cart.subscribe((quantity) => {
  panel = quantity;
});

cart.setQuantity(1);
console.log(`badge ${badge}, panel ${panel}`);

cart.unsubscribe(panelId); // the panel closes
cart.setQuantity(2);
console.log(`badge ${badge}, panel ${panel}`);
One cart. Independent views.Illustrated from the selected execution step
Cart subscriptions, callback delivery and panel cleanupBoxes identify lesson roles. Values and registrations come from recorded snapshots. Moving values represent synchronous callback calls, not messages or browser paint timing.SUBJECT · OWNS THE QUANTITYno registrationno registrationCart sessionRecorded quantity—REGISTRATIONS○ Header badge○ Cart panelHeader badgeVIEW VALUE—Not initializedCart panelVIEW VALUE—Not initialized

Press Play or Step. Setup, registrations and callbacks follow the recorded program.

Recorded values · synchronous calls · each view owns its registration