Challenge 02 — Duplicate event listeners

Find why memory grows when listeners accumulate.

Mission

Each subscribe adds another listener that closes over large data.

DevTools checklist

  1. Take snapshot (A)
  2. Click Start leaking — wait ~10 seconds
  3. Force GC → snapshot (B) → Comparison
  4. Look for Closure and listener-related growth

Expected signals

  • Closure count increases
  • Array and Object deltas grow
  • Retainers show EventTarget → handler → payload

Hints & solution

Hint

Each tick registers a new handler on the event bus. Handlers are never removed, and each one closes over a large payload array.

Solution

addEventListener is called every 500ms without a matching removeEventListener. Each handler retains its payload via closure.

// Fix: remove listeners, or use AbortController
const controller = new AbortController();
target.addEventListener('tick', handler, { signal: controller.signal });
// on cleanup: controller.abort();

← Lesson: Event listeners · All challenges