Under The Hood
frontend angular Angular 19+ (resource, rxResource)

resource() / rxResource(): Signal-Based HTTP Loading

Last updated
Prerequisites:
Angular signals and computed
Understanding of HTTP requests and observables (for rxResource)
  • angular
  • resource
  • rxResource
  • async
  • signals
  • http
  • loading

Read at your depth

The practical view

resource() is a signal-based wrapper for async loading. const user = resource({ request: () => this.userId(), loader: ({ request }) => fetch(`/users/${request}`).then(r => r.json()) });. Reading user.value() gives the data, user.isLoading(), user.error(), user.status(). When the request signal changes, the loader re-runs automatically. It also supports streaming (ArrayBuffer/ReadableStream) and state transitions. rxResource() is the RxJS-flavored version: the loader returns an Observable, which it subscribes to (with cancellation). These replace the manual pattern of writing an effect that sets a loading/data/error signal trio.

The same idea in other frameworks

react equivalent
React 19's use() with Suspense and TanStack Query's useQuery are the analogues.

Legacy vs modern

Manual effect + signals trio vs resource()

Hand-writing an effect that sets loading/data/error signals is boilerplate and race-prone; resource() handles lifecycle, races, and cancellation declaratively.

before → after
Manual async state trio
data = signal<User | null>(null);
loading = signal(false);
error = signal<Error | null>(null); // ❌ OLD: three signals + effect
resource() declarative loading
user = resource({
  request: () => this.userId(),
  loader: ({ request }) => fetch(`/users/${request}`).then((r) => r.json()),
}); // ✅ NEW

Interview gotchas

Context

This tests the race-condition handling — a core reason to use resource() — interviewers want the generation/abort mechanics, not just 'it handles it'.

The mechanical answer

Each loader invocation is associated with the request generation at the time it started. When the request computation changes, the resource bumps the generation and starts a new loader run; the previous invocation is marked stale, and its eventual resolution is ignored — it cannot write into the value slot. Depending on the loader, the old work may also be aborted (for fetch-based loaders, an AbortController is passed; for rxResource, the subscription is cancelled). This guarantees the value signal always reflects the latest request, eliminating the classic 'out-of-order response' bug that manual effect-based code suffers from. The framework also coalesces rapid request changes through the scheduler so you don't fire N parallel loaders for N quick input changes.

Trap

The naive answer is 'it just does the requests in order'. Ordering alone doesn't prevent slow-first/fast-second races; the generation guard is what discards stale results. Another trap: assuming the loader is called synchronously on every request change — it is scheduled, and intermediate request changes are coalesced. Candidates who mention generation tokens/abort and scheduler coalescing show real understanding.

Under The Hood — a multi-depth technical interview hub.

Third-party licenses Press ⌘ K to search.