Что такое 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. Он позволяет описывать асинхронную логику декларативно, безопасно и лаконично, непосредственно в шаблонах, без необходимости управлять подписками вручную.