# RxJS in Angular 2026: operatori, Subjects e interop con i Signals > RxJS in Angular 2026: operatori, Subjects e i pattern di interop con i Signals che gli sviluppatori Angular usano in produzione, più le domande da colloquio più frequenti. - Published: 2026-07-02 - Updated: 2026-07-07 - Author: SharpSkill - Tags: angular, rxjs, signals, observables, typescript, deep-dive - Reading time: 10 min --- RxJS in Angular resta la spina dorsale della programmazione asincrona nel 2026, anche mentre i Signals ridisegnano il modo in cui i componenti Angular tracciano lo stato sincrono. Sapere quando ricorrere a un Observable, quando un Signal si adatta meglio e come i due interoperano è ormai una competenza fondamentale per qualsiasi sviluppatore Angular. Questo approfondimento attraversa gli operatori, i Subjects e i pattern di interop che distinguono una risposta sicura in un colloquio da una che si blocca. > **Signals vs Observables in una riga** > > I Signals modellano lo stato sincrono che ha sempre un valore corrente; gli Observables modellano flussi asincroni di eventi nel tempo. Angular 20 mantiene entrambi, e il pacchetto `@angular/core/rxjs-interop` è il ponte ufficiale tra i due. ## Perché RxJS conta ancora accanto ai Signals di Angular L'introduzione dei Signals ha spinto molti team a chiedersi se RxJS fosse in via di dismissione. La risposta, tre versioni maggiori dopo, è no. I Signals eccellono nel rappresentare lo stato che ha un valore in questo momento: un contatore, un campo di un form, una scheda selezionata. Gli Observables eccellono nel modellare eventi che arrivano nel tempo e possono richiedere trasformazione, cancellazione o combinazione: risposte HTTP, messaggi WebSocket, input da tastiera, eventi del router e timer. Le stesse API di Angular rendono visibile questa divisione. `HttpClient` restituisce ancora Observables. I reactive form espongono `valueChanges` e `statusChanges` come Observables. Gli eventi del router scorrono attraverso un Observable. Ovunque più eventi asincroni fluiscano nello stesso canale, gli operatori RxJS restano lo strumento più espressivo a disposizione. La [guida ufficiale alla reattività di Angular](https://angular.dev/guide/signals/rxjs-interop) tratta i due modelli come complementari anziché in competizione, e la [documentazione di RxJS](https://rxjs.dev/guide/overview) rimane un riferimento obbligato per qualsiasi flusso di dati non banale. La regola pratica che regge in code review: Signals per lo stato locale del componente letto nei template, e Observables per le pipeline asincrone che richiedono operatori. Lo strato di interop collega i due, così nessun modello diventa un muro. Gli operatori di combinazione rafforzano questo punto. Coordinare più sorgenti asincrone, come fondere i parametri di rotta con i filtri salvati di un utente e un feed di prezzi in tempo reale, è esattamente ciò per cui `combineLatest`, `withLatestFrom` e `forkJoin` sono stati costruiti. I Signals possono derivare valori calcolati da altri signals, ma non possono esprimere una coordinazione basata sul tempo come "emetti solo dopo che tutte e tre le sorgenti hanno prodotto almeno un valore". Quella composizione dichiarativa nel tempo è la ragione duratura per cui RxJS resta nella cassetta degli attrezzi. ## Operatori RxJS fondamentali su cui ogni sviluppatore Angular fa affidamento Gli operatori sono funzioni pure che prendono un Observable e ne restituiscono uno nuovo. Lo scenario classico da colloquio è la ricerca durante la digitazione, perché mette all'opera quattro operatori contemporaneamente: limitare l'input, saltare i duplicati, cancellare le richieste obsolete e recuperare dagli errori. ```typescript // search.component.ts import { Component, inject } from '@angular/core'; import { FormControl, ReactiveFormsModule } from '@angular/forms'; import { HttpClient } from '@angular/common/http'; import { debounceTime, distinctUntilChanged, switchMap, catchError } from 'rxjs/operators'; import { of } from 'rxjs'; @Component({ selector: 'app-search', imports: [ReactiveFormsModule], template: ``, }) export class SearchComponent { private http = inject(HttpClient); query = new FormControl(''); results$ = this.query.valueChanges.pipe( debounceTime(300), // wait until typing pauses for 300ms distinctUntilChanged(), // ignore emissions that repeat the last value switchMap((term) => // cancel the in-flight request, start a fresh one this.http.get(`/api/search?q=${term}`).pipe( catchError(() => of([])) // recover from a failed request without killing the stream ) ) ); } ``` Il dettaglio sottile che gli intervistatori sondano è perché `switchMap` si trova al livello esterno mentre `catchError` è annidato al suo interno. Collocare `catchError` sull'Observable HTTP interno significa che una richiesta fallita restituisce un array vuoto e il flusso esterno continua a scorrere. Spostare `catchError` sulla pipe esterna terminerebbe l'intera ricerca dopo il primo errore, rompendo ogni pressione di tasto successiva. I quattro operatori di flattening rispondono ciascuno a una diversa domanda di concorrenza, e scegliere quello sbagliato è una delle fonti più comuni di race condition nelle app Angular. > **Scegliere un operatore di flattening** > > Usa `switchMap` per cancellare la richiesta interna precedente e mantenere solo l'ultima (ricerca typeahead). Usa `mergeMap` per eseguire ogni richiesta interna in concorrenza (upload paralleli indipendenti). Usa `concatMap` per accodare le richieste in ordine rigoroso (scritture sequenziali che non devono sovrapporsi). Usa `exhaustMap` per ignorare i nuovi input finché quello corrente non termina (bloccare un pulsante di invio cliccato due volte). Azzeccare questa scelta è ciò che trasforma una funzionalità instabile in una prevedibile. Il [modulo SharpSkill sugli operatori RxJS](/technologies/angular/interview-questions/rxjs-operators) allena queste distinzioni con domande a punteggio che riproducono le esatte race condition che ciascun operatore previene. ## Subjects e multicasting: BehaviorSubject, ReplaySubject e Subject Un Observable semplice è unicast: ogni subscriber innesca un'esecuzione nuova. Un Subject è al contempo un Observable e un Observer, il che lo rende multicast. Questa proprietà fa dei Subjects il modo standard per costruire uno store di stato leggero o un bus di eventi all'interno di un service. Le tre varianti differiscono per ciò che riproducono ai subscriber tardivi. Un `Subject` semplice non emette nulla dal passato, quindi un subscriber vede solo i valori che arrivano dopo che si è iscritto. Un `BehaviorSubject` memorizza il valore corrente e lo riproduce immediatamente, ed è per questo che richiede un valore iniziale. Un `ReplaySubject` bufferizza un numero configurabile di emissioni passate e le riproduce tutte. ```typescript // cart.service.ts import { Injectable } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class CartService { // BehaviorSubject holds the latest value and replays it to new subscribers private readonly itemsSubject = new BehaviorSubject([]); // expose a read-only stream, never the writable Subject itself readonly items$ = this.itemsSubject.asObservable(); add(item: string): void { const current = this.itemsSubject.value; // synchronous access to the last value this.itemsSubject.next([...current, item]); // emit the next immutable state } } ``` Qui contano due abitudini. Primo, esporre `asObservable()` anziché il Subject impedisce al codice esterno di chiamare `next()` e mutare lo stato dalla porta di servizio. Secondo, emettere un nuovo riferimento di array invece di mutare in loco mantiene prevedibili la change detection e i controlli di uguaglianza. Questo pattern precede i Signals e funziona ancora bene per lo stato condiviso e iniettabile che più componenti tra loro slegati consumano. Detto ciò, un `BehaviorSubject` usato puramente per tenere un singolo valore corrente letto nei template oggi si esprime spesso meglio come Signal scrivibile, che rimuove la sottoscrizione e la cerimonia di `asObservable()`. Il Subject resta la scelta più forte quando lo store deve anche trasformare, debounce o combinare le sue emissioni con altri flussi, dato che quegli operatori non hanno equivalente diretto tra i Signals. Decidere tra i due caso per caso, anziché ripiegare ovunque su uno, è un segno di padronanza che gli intervistatori premiano. ## Interop RxJS-Signals con toSignal e toObservable Il pacchetto di interop offre due funzioni di punta. `toSignal` converte un Observable in un Signal di sola lettura che un template può consumare direttamente, e gestisce automaticamente il ciclo di vita della sottoscrizione. `toObservable` fa l'inverso, trasformando un Signal in un Observable così che gli operatori RxJS possano elaborarne i cambiamenti. ```typescript // dashboard.component.ts import { Component, signal, inject } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { toSignal, toObservable } from '@angular/core/rxjs-interop'; import { switchMap } from 'rxjs/operators'; @Component({ selector: 'app-dashboard', template: `{{ user()?.name }}` }) export class DashboardComponent { private http = inject(HttpClient); // a Signal drives the query; toObservable turns each change into a stream event readonly userId = signal(1); private readonly user$ = toObservable(this.userId).pipe( switchMap((id) => this.http.get<{ name: string }>(`/api/users/${id}`)) ); // toSignal subscribes immediately and unsubscribes on destroy, no manual teardown readonly user = toSignal(this.user$, { initialValue: null }); } ``` L'opzione `initialValue` merita attenzione. Senza di essa, `toSignal` restituisce un Signal tipizzato come il valore oppure `undefined`, perché nulla ha ancora emesso. Passare `initialValue: null` dà al template uno stato iniziale definito e un tipo più pulito. Per sorgenti sincrone come un `BehaviorSubject`, `requireSync: true` garantisce che un valore sia disponibile alla prima lettura ed elimina del tutto `undefined` dal tipo. Questo ponte bidirezionale è ciò che permette a una codebase di adottare i Signals in modo incrementale senza riscrivere ogni pipeline Observable esistente. Il [modulo di colloquio sui Signals di Angular](/technologies/angular/interview-questions/angular-signals) copre in profondità il modello di reattività, e la [panoramica delle funzionalità dei Signals in Angular 18](/blog/angular/angular-18-signals-new-features) ricostruisce come queste primitive si sono evolute. ## Collegare gli Observables alle signal resource con rxResource Angular 20 promuove `rxResource` come il modo dichiarativo per caricare dati asincroni da un Observable in uno stato basato su signal. Traccia lo stato di caricamento, errore e risoluzione come signals, il che rimuove gran parte della contabilità manuale delle sottoscrizioni che le chiamate HTTP richiedevano un tempo. ```typescript // products.component.ts import { Component, signal, inject } from '@angular/core'; import { rxResource } from '@angular/core/rxjs-interop'; import { HttpClient } from '@angular/common/http'; @Component({ selector: 'app-products', template: `` }) export class ProductsComponent { private http = inject(HttpClient); readonly category = signal('laptops'); // Angular 20 renamed the fields: request -> params and loader -> stream readonly products = rxResource({ params: () => ({ category: this.category() }), stream: ({ params }) => this.http.get(`/api/products?category=${params.category}`), }); // products.value(), products.isLoading() and products.error() are all signals } ``` La rinomina da `request`/`loader` in Angular 19 a `params`/`stream` in Angular 20 è un inciampo frequente, quindi menzionare i nomi attuali dei campi segnala conoscenze aggiornate in un colloquio. Quando il signal `category` cambia, `rxResource` cancella la richiesta precedente e avvia un nuovo stream, offrendo gratuitamente la semantica di `switchMap`. Il [riferimento all'API rxResource](https://v20.angular.dev/api/core/rxjs-interop/rxResource) documenta l'intero ciclo di vita dello stato. ## Evitare i memory leak con takeUntilDestroyed Qualsiasi `subscribe()` manuale che sopravvive al proprio componente causa un memory leak e può far scattare callback contro una view distrutta. La correzione storica era il pattern `takeUntil(this.destroy$)` con un Subject completato in `ngOnDestroy`. Angular ha sostituito quel boilerplate con `takeUntilDestroyed`, un operatore che lega la sottoscrizione all'injection context corrente o a un `DestroyRef` esplicito. ```typescript // live-feed.component.ts import { Component, inject, DestroyRef } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { interval } from 'rxjs'; @Component({ selector: 'app-live-feed', template: `` }) export class LiveFeedComponent { private destroyRef = inject(DestroyRef); constructor() { interval(1000) // completes the subscription automatically when the component is destroyed .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe((tick) => console.log('tick', tick)); } } ``` Chiamato dentro un costruttore o un inizializzatore di campo, `takeUntilDestroyed()` non necessita di argomenti perché legge il `DestroyRef` ambientale. Chiamato altrove, come dentro un metodo, richiede un `DestroyRef` esplicito passato in ingresso, come mostrato sopra. Preferire `toSignal` e `rxResource` evita del tutto le sottoscrizioni manuali, che è il modo più pulito per aggirare i leak; quando serve davvero una sottoscrizione grezza, `takeUntilDestroyed` è la guardia corretta. > **Il tranello dell'injection context** > > Chiamare `takeUntilDestroyed()` senza argomenti fuori da un injection context lancia un errore a runtime. Sottoscrivere dentro un event handler, un `setTimeout` o una callback RxJS è fuori da quel context, quindi il `DestroyRef` va catturato prima (tipicamente come campo iniettato) e passato esplicitamente. Dimenticarlo è la causa più frequente dell'errore "takeUntilDestroyed() can only be used within an injection context". ## Domande da colloquio su Angular e RxJS che vale la pena ripassare Gli intervistatori tendono a girare attorno agli stessi argomenti ad alto segnale. Saper rispondere a questi in modo netto separa i candidati che hanno usato RxJS da quelli che ne hanno solo letto. - **Observables hot contro cold.** Un Observable cold avvia il suo producer per ogni sottoscrizione, quindi ogni subscriber ottiene un'esecuzione indipendente (una richiesta HTTP nuova). Un Observable hot condivide un solo producer tra i subscriber, ed è ciò che creano i Subjects e `share()`. - **switchMap contro mergeMap contro concatMap contro exhaustMap.** `switchMap` cancella l'Observable interno precedente (ideale per la ricerca). `mergeMap` esegue tutti gli Observable interni in concorrenza. `concatMap` li accoda in ordine. `exhaustMap` ignora i nuovi input mentre uno è in esecuzione (ideale per prevenire i doppi invii di form). - **Come prevenire i memory leak.** Preferire `toSignal`, `rxResource` o la pipe `async`, che si disiscrivono tutte automaticamente; per le sottoscrizioni manuali, usare `takeUntilDestroyed`. - **Quando convertire un Observable in un Signal.** Ricorrere a `toSignal` quando un template ha bisogno di un valore in modo sincrono e la sorgente è asincrona, lasciando che la change detection legga l'ultimo valore senza una pipe `async`. Lavorare su versioni a punteggio di queste sotto pressione di tempo è il modo più rapido per interiorizzarle. L'[hub tecnologico Angular](/technologies/angular) raggruppa i moduli su RxJS, Signals e change detection che mappano direttamente su queste domande. ## Conclusione RxJS e Signals sono partner nello stack Angular moderno, non rivali. Le conclusioni che vale la pena portarsi nel prossimo progetto o colloquio: - Usare i Signals per lo stato sincrono del componente e gli Observables per le pipeline di eventi asincroni che richiedono operatori. - Ricorrere a `switchMap` per cancellare il lavoro obsoleto, a `exhaustMap` per bloccare i duplicati, e tenere `catchError` sull'Observable interno così che il flusso esterno sopravviva ai fallimenti. - Esporre lo stato dei Subject tramite `asObservable()` ed emettere valori immutabili per mantenere prevedibile la change detection. - Collegare i due modelli con `toSignal` e `toObservable`, e passare `initialValue` o `requireSync` per tipi puliti. - Caricare dati asincroni in modo dichiarativo con `rxResource` in Angular 20, ricordando i nomi dei campi `params` e `stream`. - Proteggere ogni sottoscrizione manuale residua con `takeUntilDestroyed` per eliminare i leak. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/it/blog/angular/rxjs-angular-operators-subjects-signals-interop