Wenn du in Angular mit asynchronen Datenquellen (wie HTTP-Anfragen oder Timern) arbeitest, erhältst du meistens ein Observable oder ein Promise. Um deren Werte im Template anzuzeigen, müsstest du sie in der Komponente manuell abonnieren (.subscribe()) und den Wert in einer Variable speichern. Die AsyncPipe nimmt dir genau diese Arbeit ab und sorgt automatisch für sauberes Aufräumen.
Warum die AsyncPipe nutzen?
Das manuelle Abonnieren (Subscribe) von Observables birgt ein Risiko: Memory Leaks. Wenn eine Komponente zerstört wird (z. B. beim Navigieren auf eine andere Seite) und du vergessen hast, das Observable zu entabonnieren (.unsubscribe()), läuft es im Hintergrund weiter.
Die AsyncPipe (| async) löst dieses Problem elegant:
- Sie abonniert das Observable/Promise automatisch, sobald die Komponente gerendert wird.
- Sie entabonniert (unsubscribe) es automatisch, sobald die Komponente zerstört wird.
- Sie aktualisiert die View (löst die Change Detection aus), sobald neue Daten eintreffen.
import { Component } from '@angular/core';
import { AsyncPipe } from '@angular/common'; // Import notwendig!
import { Observable, interval, map } from 'rxjs';
@Component({
selector: 'app-timer',
template: `
<div>
<!-- Die Pipeline "async" entpackt das Observable -->
Aktuelle Zeit: {{ time$ | async }}
</div>
`,
standalone: true,
imports: [AsyncPipe]
})
export class TimerComponent {
// Ein Observable, das jede Sekunde feuert (Namenskonvention: endet mit $)
time$: Observable<string> = interval(1000).pipe(
map(() => new Date().toLocaleTimeString())
);
}AsyncPipe mit Promises
Neben Observables (aus RxJS) funktioniert die AsyncPipe auch nativ mit JavaScript Promises. Das Prinzip ist exakt dasselbe.
import { Component } from '@angular/core';
import { AsyncPipe } from '@angular/common';
@Component({
selector: 'app-greeting',
template: `
<p>
Nachricht: {{ greetingPromise | async }}
</p>
`,
standalone: true,
imports: [AsyncPipe]
})
export class GreetingComponent {
greetingPromise = new Promise<string>((resolve) => {
setTimeout(() => resolve('Hallo aus der Zukunft!'), 2000);
});
}Werte speichern mit @if und as
Häufig möchtest du denselben asynchronen Wert an mehreren Stellen im Template verwenden (z.B. den Namen des Users mehrmals ausgeben oder Eigenschaften eines Objekts auslesen).
Würdest du user$ | async jedes Mal neu schreiben, würde das Observable jedes Mal neu abonniert werden (was bei HTTP-Requests fatal wäre!).
Die Lösung ist die Kombination aus dem @if Control Flow und dem as Keyword. Damit speicherst du den entpackten Wert in einer lokalen Template-Variable.
<!-- Das Observable user$ wird abonniert. -->
<!-- Ist der Wert "truthy" (z.B. das geladene Objekt), wird er in "user" gespeichert. -->
@if (user$ | async; as user) {
<div class="profile-card">
<h2>{{ user.name }}</h2>
<p>Email: {{ user.email }}</p>
<img [src]="user.avatarUrl" alt="Avatar">
</div>
} @else {
<p>Lade Benutzerdaten...</p>
}Wissenswertes zur AsyncPipe
Impure Pipe
Die AsyncPipe ist intern als impure markiert. Das bedeutet, sie läuft bei jedem Change-Detection-Zyklus an und prüft, ob der Stream eine neue Emission geliefert hat. Bei OnPush-Komponenten ist das ein Vorteil: Sobald ein neuer Wert kommt, ruft die Pipe selbst markForCheck() auf und stößt das Re-Rendern an.
null vor der ersten Emission
Solange das Observable oder Promise noch keinen Wert geliefert hat, gibt die Pipe null zurück. Der TypeScript-Typ ist daher z. B. string | null. In Templates musst du diesen Fall explizit abfangen, etwa mit @if oder einem Fallback ({{ name$ | async ?? ‘Lade…’ }}).
Mehrfache Subscriptions vermeiden
Jedes | async erzeugt eine eigene Subscription. Wenn du user$ | async dreimal im Template schreibst, wird der HTTP-Request auch dreimal abgesetzt. Speichere den entpackten Wert daher mit @if (user$ | async; as user) oder seit Angular 18 mit @let user = user$ | async; in einer Template-Variablen.
Stream-Wechsel räumt auf
Wechselt die Referenz des Observables (z. B. weil ein @Input neue Daten triggert), unsubscribed die Pipe automatisch vom alten Stream und abonniert den neuen. Das verhindert Leaks beim Routing- oder Filterwechsel — kann aber kurz fehlende Emissionen zeigen, wenn beide Streams hintereinander getauscht werden.
Moderne Alternative: toSignal
Seit Angular 16 gibt es toSignal() aus @angular/core/rxjs-interop. Es konvertiert ein Observable in ein Signal, das im Template ohne Pipe direkt mit {{ user() }} ausgelesen wird, sich gut mit computed() kombiniert und sauberer in zoneless Apps funktioniert. Für reine Promises bleibt die AsyncPipe aber weiterhin ein praktischer Shortcut.