All topics
Performanceintermediate

Memoization with Computed Signals

Explain how computed() signals provide automatic memoization as a performance technique distinct from manual caching.

Memoization — caching an expensive computation's result and reusing it as long as its inputs haven't changed — is a classic performance technique, and computed() signals give Angular developers this behavior essentially for free, without hand-writing a manual cache, which is worth understanding specifically as a performance topic distinct from just knowing computed()'s basic syntax (covered separately under Computed Signals in the state group).

A plain getter is like recalculating your monthly budget from scratch every single time someone asks how much you're spending, even if nothing has changed since the last time you checked; a computed signal is like keeping a running total that only updates when an actual transaction occurs, instantly handing over the same correct number for every question in between.

Key Concepts

1
Before Signals, achieving this same memoization required either a manual caching pattern (storing a previous input/output pair and comparing on each call) or accepting the cost of an expensive plain getter re-running on every single change detection check, regardless of whether its actual inputs changed — a real, measurable cost for genuinely expensive derivations (heavy filtering/sorting of large lists, complex aggregate calculations) evaluated on every check across a large component tree.
2
computed()'s automatic dependency tracking means the memoization is also more precise than most hand-rolled caching would typically bother to be: it only recalculates when one of the *actual* Signals read during its last execution changes, not on any broader, coarser-grained "something in the component changed" signal — this fine-grained precision is what lets you compose several computed() signals together (a computed reading other computeds) without each layer redundantly recalculating whenever an unrelated dependency elsewhere changes.
computed()
3
A sharp interview answer contrasts this with the specific, common anti-pattern it replaces: a plain getter method (get filteredItems() { return this.items.filter(...) }) bound in a template re-executes on every single change detection check for that component, which for a large list with an expensive filter/sort operation can become a genuine, measurable performance problem — replacing that getter with a computed() signal (or, in a pre-Signals codebase, a manually memoized method) is a concrete, correct fix worth being able to describe precisely in an interview.
get filteredItems() { return this.items.filter(...) }computed()