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:

  1. Sie abonniert das Observable/Promise automatisch, sobald die Komponente gerendert wird.
  2. Sie entabonniert (unsubscribe) es automatisch, sobald die Komponente zerstört wird.
  3. Sie aktualisiert die View (löst die Change Detection aus), sobald neue Daten eintreffen.
TypeScript async-pipe.ts
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.

TypeScript promise.ts
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.

HTML if-as.html
<!-- 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.

Weiterführende Ressourcen

Externe Quellen

/ Weiter

Zurück zu Pipes

Zur Übersicht