The resource API turns an async loader into signals for value, status and error. A request signal describes the inputs, and when it changes the resource reloads and cancels the previous run, which removes the manual cancel-and-ignore pattern that race conditions required.
Before you start
You should be comfortable with signals and async data loading. This article assumes a recent Angular version that exposes resource; the pattern also appears as httpResource for HTTP.
Step-by-step walkthrough
Step 1: Describe the request as a signal
request: () => ({ id: this.id() }) makes the resource reload whenever id changes. Because the request is a signal, there is no imperative load call to forget, and the dependency is explicit.
Step 2: Load with cancellation semantics
The loader receives the request and returns a promise. When the request changes before the previous load finishes, Angular cancels the stale run, so an old response cannot overwrite a newer one. This is the same race protection as keying a fetch, but built into the primitive.
Step 3: Read status and value as signals
value(), status() and error() are signals the template reads. A loading state is status() === 'loading', and an error state reads error(). Because they are signals, change detection updates only the consumers, and derived values such as a retry button can compute from them.
Worked scenario
The resource reloads and cancels when the id changes.
import { signal, resource } from '@angular/core';
const id = signal('1');
const user = resource({
request: () => ({ id: id() }),
loader: async ({ request, abortSignal }) => {
const res = await fetch(`/api/users/${request.id}`, { signal: abortSignal });
return res.json() as Promise<{ name: string }>;
},
});Walk through the example
Reading user.value() yields the loaded user, and changing id triggers a reload with the new request. The abortSignal cancels the previous fetch, so if the user switches ids quickly, only the latest result is applied. The status signal drives a spinner without extra flags.
Common mistake
Calling the loader imperatively or duplicating the request in a useEffect-style pattern, which reintroduces races the resource already prevents. Another is ignoring the abortSignal inside the loader, so the stale fetch keeps running and its result may arrive late.
Verify the behavior
Change the request id twice quickly and assert only the final value appears, proving stale cancellation. Assert status() is loading while in flight and error is populated when the loader rejects. Confirm that a failed load leaves the previous value intact or cleared as you intend.
Interview exercise
How does resource prevent a stale response from overwriting newer data?
Answer and reasoning
When the request signal changes, the resource starts a new run and cancels the previous one through the abortSignal. A cancelled run is discarded, so its late result cannot be applied. This is the reactive equivalent of attaching a request key to each fetch and ignoring responses whose key no longer matches.
Continue learning
Compare fetching patterns in Angular signal derived values and HTTP error handling and retry. Read the Angular resource guide and try the Angular interview questions.