Question presented to candidate: "Users report the dashboard gets slower every time they navigate away and back, and the logs show the same handler firing several times per event. How would you find and fix subscription leaks in an Angular 22 app?"
What a strong answer should cover:
- Why it leaks: a
subscribe()to a long-lived source (a root service Subject, router events,interval, a websocket) keeps running after the component is destroyed; its callback keeps the component in memory and fires on every event. - What does not need cleanup: sources that complete by themselves, such as a single
HttpClientcall ortake(1), release on completion. - Preferred fixes: let Angular own the subscription: the async pipe,
toSignal(), ortakeUntilDestroyed()(in an injection context, ortakeUntilDestroyed(destroyRef)elsewhere).DestroyRef.onDestroyfor anything else. - Older patterns still valid: a
destroy$Subject withtakeUntil, or aSubscriptionbag unsubscribed inngOnDestroy. - Operator placement:
takeUntilmust be the last operator, afterswitchMapand friends, or the inner subscription survives. - How to detect: count active subscriptions in a test after
fixture.destroy(), heap snapshots for detached components, and lint rules.
Clarifying questions expected:
- "Which sources does the dashboard subscribe to: HTTP only, or store and socket streams too?"
- "Is it signal-based already, or
subscribe()inngOnInit?"
Code / implementation expected: Yes β several subscription styles in one component, with live subscriptions counted after it is destroyed.