# RxJS in Angular 2026: Operatoren, Subjects und Signals-Interop > RxJS in Angular 2026: Operatoren, Subjects und die Signals-Interop-Muster, die Angular-Entwickler in Produktion nutzen, plus die häufigsten Interviewfragen. - Published: 2026-07-02 - Updated: 2026-07-07 - Author: SharpSkill - Tags: angular, rxjs, signals, observables, typescript, deep-dive - Reading time: 10 min --- RxJS bleibt in Angular auch 2026 das Rückgrat der asynchronen Programmierung, selbst wenn Signals die Art und Weise verändern, wie Angular-Komponenten synchronen Zustand verfolgen. Zu wissen, wann ein Observable die richtige Wahl ist, wann ein Signal besser passt und wie beide zusammenspielen, gehört heute zum Kernwissen jedes Angular-Entwicklers. Dieser Deep Dive führt durch die Operatoren, Subjects und Interop-Muster, die eine souveräne Interviewantwort von einer ins Stocken geratenen unterscheiden. > **Signals vs. Observables in einem Satz** > > Signals modellieren synchronen Zustand, der immer einen aktuellen Wert hat; Observables modellieren asynchrone Ereignisströme über die Zeit. Angular 20 behält beide bei, und das Paket `@angular/core/rxjs-interop` ist die offizielle Brücke zwischen ihnen. ## Warum RxJS neben Angular Signals weiterhin zählt Die Einführung von Signals ließ viele Teams fragen, ob RxJS auf dem Weg nach draußen sei. Die Antwort lautet drei Hauptversionen später: nein. Signals glänzen bei Zustand, der jetzt einen Wert hat: ein Zähler, ein Formularfeld, ein ausgewählter Tab. Observables glänzen bei Ereignissen, die über die Zeit eintreffen und Transformation, Abbruch oder Kombination erfordern: HTTP-Antworten, WebSocket-Nachrichten, Tastatureingaben, Router-Ereignisse und Timer. Angulars eigene APIs machen diese Trennung sichtbar. `HttpClient` liefert nach wie vor Observables. Reactive Forms stellen `valueChanges` und `statusChanges` als Observables bereit. Router-Ereignisse fließen durch ein Observable. Überall dort, wo mehrere asynchrone Ereignisse durch denselben Kanal strömen, bleiben RxJS-Operatoren das ausdrucksstärkste verfügbare Werkzeug. Der offizielle [Angular-Leitfaden zur Reaktivität](https://angular.dev/guide/signals/rxjs-interop) behandelt beide Modelle als ergänzend statt konkurrierend, und die [RxJS-Dokumentation](https://rxjs.dev/guide/overview) bleibt eine Pflichtreferenz für jeden nicht-trivialen Datenfluss. Die praktische Regel, die im Code-Review Bestand hat: Signals für lokalen, im Template gelesenen Komponentenzustand und Observables für asynchrone Pipelines, die Operatoren benötigen. Die Interop-Schicht verbindet beide, sodass kein Modell zur Mauer wird. Kombinationsoperatoren untermauern diesen Punkt. Mehrere asynchrone Quellen zu koordinieren, etwa Routenparameter mit den gespeicherten Filtern eines Nutzers und einem Live-Preis-Feed zu verschmelzen, ist genau das, wofür `combineLatest`, `withLatestFrom` und `forkJoin` gebaut wurden. Signals können berechnete Werte aus anderen Signals ableiten, aber sie können keine zeitbasierte Koordination ausdrücken wie "gib erst dann aus, wenn alle drei Quellen mindestens einen Wert geliefert haben". Diese deklarative Komposition über die Zeit ist der dauerhafte Grund, warum RxJS im Werkzeugkasten bleibt. ## Zentrale RxJS-Operatoren, auf die sich jeder Angular-Entwickler verlässt Operatoren sind reine Funktionen, die ein Observable entgegennehmen und ein neues zurückgeben. Das klassische Interviewszenario ist die Suche während der Eingabe, weil es vier Operatoren auf einmal beansprucht: die Eingabe drosseln, Duplikate überspringen, veraltete Anfragen abbrechen und Fehler abfangen. ```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 ) ) ); } ``` Das feine Detail, das Interviewer abklopfen, ist, warum `switchMap` auf der äußeren Ebene sitzt, während `catchError` in ihm verschachtelt ist. `catchError` auf das innere HTTP-Observable zu setzen bedeutet, dass eine fehlgeschlagene Anfrage ein leeres Array zurückgibt und der äußere Stream weiterfließt. Würde `catchError` in die äußere Pipe verschoben, beendete es die gesamte Suche nach dem ersten Fehler und bräche jeden folgenden Tastendruck. Die vier Flattening-Operatoren beantworten jeweils eine andere Nebenläufigkeitsfrage, und die falsche Wahl ist eine der häufigsten Ursachen für Race Conditions in Angular-Anwendungen. > **Die Wahl eines Flattening-Operators** > > `switchMap` bricht die vorherige innere Anfrage ab und behält nur die neueste (Typeahead-Suche). `mergeMap` führt jede innere Anfrage nebenläufig aus (unabhängige parallele Uploads). `concatMap` reiht Anfragen in strikter Reihenfolge auf (sequenzielle Schreibvorgänge, die sich nicht überschneiden dürfen). `exhaustMap` ignoriert neue Eingaben, bis die aktuelle abgeschlossen ist (Blockieren eines doppelt geklickten Absende-Buttons). Diese Wahl richtig zu treffen, macht aus einem instabilen Feature ein vorhersagbares. Das [SharpSkill-Modul zu RxJS-Operatoren](/technologies/angular/interview-questions/rxjs-operators) trainiert diese Unterschiede mit bewerteten Fragen, die genau die Race Conditions reproduzieren, die jeder Operator verhindert. ## Subjects und Multicasting: BehaviorSubject, ReplaySubject und Subject Ein einfaches Observable ist Unicast: Jeder Subscriber löst eine frische Ausführung aus. Ein Subject ist zugleich Observable und Observer, was es zum Multicast macht. Diese Eigenschaft macht Subjects zum Standardweg, um einen leichtgewichtigen State-Store oder einen Event-Bus innerhalb eines Service aufzubauen. Die drei Varianten unterscheiden sich darin, was sie späten Subscribern erneut ausspielen. Ein einfaches `Subject` gibt nichts aus der Vergangenheit aus, sodass ein Subscriber nur Werte sieht, die nach dem Abonnieren eintreffen. Ein `BehaviorSubject` speichert den aktuellen Wert und spielt ihn sofort erneut aus, weshalb es einen Initialwert benötigt. Ein `ReplaySubject` puffert eine konfigurierbare Anzahl vergangener Ausgaben und spielt sie alle erneut aus. ```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 } } ``` Zwei Gewohnheiten sind hier entscheidend. Erstens verhindert das Bereitstellen von `asObservable()` statt des Subjects, dass externer Code `next()` aufruft und Zustand durch die Hintertür verändert. Zweitens hält das Ausgeben einer neuen Array-Referenz statt einer In-Place-Mutation die Change Detection und Gleichheitsprüfungen vorhersagbar. Dieses Muster ist älter als Signals und funktioniert für geteilten, injizierbaren Zustand, den mehrere voneinander unabhängige Komponenten konsumieren, weiterhin gut. Allerdings lässt sich ein `BehaviorSubject`, das rein dazu dient, einen einzelnen aktuellen Wert für das Template zu halten, heute oft besser als beschreibbares Signal ausdrücken, was das Abonnement und die `asObservable()`-Zeremonie entfällt. Das Subject bleibt die stärkere Wahl, wenn der Store seine Ausgaben zusätzlich transformieren, entprellen oder mit anderen Streams kombinieren muss, da diese Operatoren kein direktes Signal-Äquivalent haben. Fallweise zwischen beiden zu entscheiden, statt überall auf eines zurückzufallen, ist ein Zeichen von Souveränität, das Interviewer belohnen. ## RxJS-Signals-Interop mit toSignal und toObservable Das Interop-Paket liefert zwei Hauptfunktionen. `toSignal` wandelt ein Observable in ein schreibgeschütztes Signal um, das ein Template direkt konsumieren kann, und verwaltet den Lebenszyklus des Abonnements automatisch. `toObservable` macht das Umgekehrte und verwandelt ein Signal in ein Observable, sodass RxJS-Operatoren seine Änderungen verarbeiten können. ```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 }); } ``` Die Option `initialValue` verdient Beachtung. Ohne sie gibt `toSignal` ein Signal zurück, das als der Wert oder `undefined` typisiert ist, weil noch nichts ausgegeben wurde. Die Übergabe von `initialValue: null` gibt dem Template einen definierten Anfangszustand und einen saubereren Typ. Für synchrone Quellen wie ein `BehaviorSubject` garantiert `requireSync: true`, dass beim ersten Lesen ein Wert verfügbar ist, und entfernt `undefined` vollständig aus dem Typ. Diese bidirektionale Brücke ist es, die einer Codebasis erlaubt, Signals schrittweise einzuführen, ohne jede bestehende Observable-Pipeline neu zu schreiben. Das begleitende [Angular-Signals-Interviewmodul](/technologies/angular/interview-questions/angular-signals) behandelt das Reaktivitätsmodell in der Tiefe, und der [Überblick über die Angular-18-Signals-Features](/blog/angular/angular-18-signals-new-features) zeichnet nach, wie sich diese Primitive entwickelt haben. ## Observables mit rxResource zu Signal-Ressourcen überbrücken Angular 20 hebt `rxResource` als deklarativen Weg hervor, asynchrone Daten aus einem Observable in signalbasierten Zustand zu laden. Es verfolgt Lade-, Fehler- und Auflösungsstatus als Signals, was den Großteil der manuellen Abonnement-Buchführung entfernt, die HTTP-Aufrufe früher erforderten. ```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 } ``` Die Umbenennung von `request`/`loader` in Angular 19 zu `params`/`stream` in Angular 20 ist ein häufiger Stolperstein, sodass das Nennen der aktuellen Feldnamen im Interview aktuelles Wissen signalisiert. Wenn sich das `category`-Signal ändert, bricht `rxResource` die vorherige Anfrage ab und startet einen neuen Stream, wodurch `switchMap`-Semantik gratis mitgeliefert wird. Die [rxResource-API-Referenz](https://v20.angular.dev/api/core/rxjs-interop/rxResource) dokumentiert den vollständigen Status-Lebenszyklus. ## Memory Leaks mit takeUntilDestroyed vermeiden Jedes manuelle `subscribe()`, das seine Komponente überlebt, verursacht ein Memory Leak und kann Callbacks gegen eine zerstörte View feuern. Die historische Lösung war ein `takeUntil(this.destroy$)`-Muster mit einem in `ngOnDestroy` abgeschlossenen Subject. Angular ersetzte diesen Boilerplate durch `takeUntilDestroyed`, einen Operator, der das Abonnement an den aktuellen Injection-Kontext oder eine explizite `DestroyRef` bindet. ```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)); } } ``` Innerhalb eines Konstruktors oder eines Feld-Initialisierers aufgerufen, benötigt `takeUntilDestroyed()` kein Argument, weil es die umgebende `DestroyRef` liest. An jeder anderen Stelle aufgerufen, etwa innerhalb einer Methode, erfordert es eine explizit übergebene `DestroyRef`, wie oben gezeigt. `toSignal` und `rxResource` zu bevorzugen vermeidet manuelle Abonnements vollständig, was der sauberste Weg ist, Leaks zu umgehen; wird ein rohes Abonnement wirklich benötigt, ist `takeUntilDestroyed` der korrekte Schutz. > **Die Falle mit dem Injection-Kontext** > > `takeUntilDestroyed()` ohne Argument außerhalb eines Injection-Kontexts aufzurufen, wirft einen Laufzeitfehler. Ein Abonnement innerhalb eines Event-Handlers, eines `setTimeout` oder eines RxJS-Callbacks liegt außerhalb dieses Kontexts, sodass die `DestroyRef` früher erfasst (typischerweise als injiziertes Feld) und explizit übergeben werden muss. Das zu vergessen ist die häufigste Ursache des Fehlers "takeUntilDestroyed() can only be used within an injection context". ## Angular-RxJS-Interviewfragen, die eine Wiederholung wert sind Interviewer kreisen tendenziell um dieselben aussagekräftigen Themen. Diese knapp beantworten zu können, trennt Kandidaten, die RxJS eingesetzt haben, von jenen, die nur darüber gelesen haben. - **Hot versus Cold Observables.** Ein Cold Observable startet seinen Producer pro Abonnement, sodass jeder Subscriber eine unabhängige Ausführung erhält (eine frische HTTP-Anfrage). Ein Hot Observable teilt einen Producer über Subscriber hinweg, was Subjects und `share()` erzeugen. - **switchMap versus mergeMap versus concatMap versus exhaustMap.** `switchMap` bricht das vorherige innere Observable ab (ideal für die Suche). `mergeMap` führt alle inneren Observables nebenläufig aus. `concatMap` reiht sie der Reihe nach auf. `exhaustMap` ignoriert neue Eingaben, während eine läuft (ideal, um doppeltes Absenden von Formularen zu verhindern). - **Wie man Memory Leaks verhindert.** `toSignal`, `rxResource` oder die `async`-Pipe bevorzugen, die alle automatisch abbestellen; für manuelle Abonnements `takeUntilDestroyed` nutzen. - **Wann man ein Observable in ein Signal umwandelt.** Zu `toSignal` greifen, wenn ein Template einen Wert synchron benötigt und die Quelle asynchron ist, sodass die Change Detection den neuesten Wert ohne `async`-Pipe lesen kann. Bewertete Varianten dieser Fragen unter Zeitdruck durchzuarbeiten, ist der schnellste Weg, sie zu verinnerlichen. Der [Angular-Technologie-Hub](/technologies/angular) bündelt die Module zu RxJS, Signals und Change Detection, die direkt auf diese Fragen abbilden. ## Fazit RxJS und Signals sind Partner im modernen Angular-Stack, keine Rivalen. Die Erkenntnisse, die es sich lohnt, ins nächste Projekt oder Interview mitzunehmen: - Signals für synchronen Komponentenzustand nutzen und Observables für asynchrone Ereignis-Pipelines, die Operatoren benötigen. - Zu `switchMap` greifen, um veraltete Arbeit abzubrechen, zu `exhaustMap`, um Duplikate zu blockieren, und `catchError` auf dem inneren Observable halten, damit der äußere Stream Fehler überlebt. - Subject-Zustand über `asObservable()` bereitstellen und unveränderliche Werte ausgeben, um die Change Detection vorhersagbar zu halten. - Die beiden Modelle mit `toSignal` und `toObservable` überbrücken und `initialValue` oder `requireSync` für saubere Typen übergeben. - Asynchrone Daten deklarativ mit `rxResource` in Angular 20 laden und dabei die Feldnamen `params` und `stream` beachten. - Jedes verbleibende manuelle Abonnement mit `takeUntilDestroyed` absichern, um Leaks zu beseitigen. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/de/blog/angular/rxjs-angular-operators-subjects-signals-interop