Что такое async pipe и зачем он нужен?

async pipe в Angular — это встроенный пайп (pipe), предназначенный для работы с асинхронными источниками данных, такими как Observable и Promise, непосредственно в шаблоне компонента. Он автоматически подписывается на поток и отписывается при уничтожении компонента, избавляя разработчика от необходимости ручного управления подписками.

1. Назначение async pipe

  • Автоматическое извлечение значения из Observable или Promise.

  • Упрощение шаблонов и устранение необходимости в ручной подписке (subscribe()).

  • Автоматическое управление подпиской и её отпиской (важно для предотвращения утечек памяти).

  • Поддержка реактивного подхода к построению UI.

2. Использование с Observable

Вместо того чтобы вручную подписываться в ngOnInit() и присваивать значение в переменную:

ngOnInit() {
this.api.getUser().subscribe(user => {
this.user = user;
});
}

Можно просто:

user$ = this.api.getUser();

И в шаблоне:

<div \*ngIf="user$ | async as user">
{{ user.name }}
</div>

3. Использование с Promise

userPromise = this.api.getUserPromise();
<div \*ngIf="userPromise | async as user">
{{ user.name }}
</div>

4. Как работает async pipe

Внутри он выполняет:

  • подписку на Observable через subscribe();

  • либо вызов then() у Promise;

  • обновление DOM при каждом изменении значения;

  • автоматическую отписку при уничтожении компонента (для Observable).

Поведение:

  • Если поток передаёт новое значение — шаблон обновляется.

  • Если поток завершён или компонент уничтожен — подписка закрывается.

5. Пример с HTTP-запросом

user$ = this.http.get<User>('/api/user');
<div \*ngIf="user$ | async as user">
<h1>{{ user.name }}</h1>
</div>

Это избавляет от необходимости вручную управлять подписками и использовать переменные-состояния.

6. Пример с BehaviorSubject

user$ = new BehaviorSubject<User>(initialUser);

В шаблоне:

<p>{{ user$ | async?.name }}</p>

Если значение обновляется:

this.user$.next({ name: 'Updated' });

DOM автоматически обновится.

7. Использование с ngFor

<ul>
<li \*ngFor="let item of items$ | async">{{ item.name }}</li>
</ul>

8. Совмещение с ngIf, ngSwitch, ngClass, ngStyle

<div \[ngClass\]="{ active: (status$ | async) === 'active' }">...</div>
<div \*ngIf="status$ | async as status">
<ng-container \[ngSwitch\]="status">
<p \*ngSwitchCase="'loading'">Загрузка...</p>
<p \*ngSwitchCase="'error'">Ошибка</p>
<p \*ngSwitchDefault>Готово</p>
</ng-container>
</div>

9. Поведение async pipe при множественных подписках

Если в шаблоне используется async несколько раз на один и тот же поток, Angular кэширует результат, чтобы не создавать новые подписки:

<div>{{ user$ | async?.name }}</div>
<div>{{ user$ | async?.email }}</div>

Однако это работает не с любым Observable — лучше использовать *ngIf="user$ | async as user":

<div \*ngIf="user$ | async as user">
<p>{{ user.name }}</p>
<p>{{ user.email }}</p>
</div>

Так создаётся одна подписка, значение доступно в блоке.

10. Особенности поведения с null и undefined

Если Observable возвращает null или undefined, async pipe всё равно отобразит это значение, и шаблон будет пересчитан. Это позволяет использовать пайп в сочетании с безопасной навигацией (?.):

<p>{{ user$ | async?.address?.city }}</p>

11. Потенциальные ошибки

  • Использование async вне контекста Observable или Promise вызовет ошибку.

  • Если Observable "холодный" (не multicasted), каждый async создаёт отдельную подписку.

  • Для комбинирования нескольких Observable лучше использовать combineLatest, forkJoin и *ngIf="combined$ | async as data".

12. Совмещение с RxJS операторами

Обычно async pipe используют совместно с RxJS в компоненте:

data$ = this.service.getData().pipe(
map(items => items.filter(x => x.active)),
catchError(() => of(\[\]))
);

И в шаблоне:

<ul>
<li \*ngFor="let item of data$ | async">{{ item.name }}</li>
</ul>

13. async и Change Detection

async pipe запускает markForCheck() внутри ChangeDetectorRef, что позволяет ему работать даже с ChangeDetectionStrategy.OnPush.

Это позволяет обновлять компонент при приходе нового значения без явного вызова detectChanges().

14. Комбинация с LetDirective (RxAngular)

Если необходимо улучшить производительность и избежать лишних подписок, можно использовать сторонние директивы (*rxLet, *ngrxLet), но async остаётся стандартным решением во встроенном API Angular.

async pipe — ключевой инструмент для реактивной архитектуры Angular. Он позволяет описывать асинхронную логику декларативно, безопасно и лаконично, непосредственно в шаблонах, без необходимости управлять подписками вручную.