What is AsyncPipe in Angular?
1. What is AsyncPipe?
AsyncPipe is a built-in Angular pipe used in templates to subscribe to an Observable or Promise and display its latest value automatically.
For an Observable, it also unsubscribes automatically when the component/view is destroyed.
Instead of manually writing:
this.productService.getProducts()
.subscribe(products => {
this.products = products;
});
you can keep the Observable:
products$ = this.productService.getProducts();
and use:
{{ products$ | async }}
or:
@for (product of products$ | async; track product.id) {
<p>{{ product.name }}</p>
}
AsyncPipe= subscribe automatically + return latest value + manage subscription cleanup.
2. Full Simple Angular Program
Suppose we have an ASP.NET Core Web API:
GET /api/products
returning products.
product.ts
export interface Product {
id: number;
name: string;
price: number;
}
product.service.ts
import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Product } from './product';
@Injectable({
providedIn: 'root'
})
export class ProductService {
private http = inject(HttpClient);
private apiUrl =
'https://localhost:7001/api/products';
getProducts(): Observable<Product[]> {
return this.http.get<Product[]>(
this.apiUrl
);
}
}
product-list.ts
import { AsyncPipe } from '@angular/common';
import { Component, inject } from '@angular/core';
import { Observable } from 'rxjs';
import { Product } from './product';
import { ProductService } from './product.service';
@Component({
selector: 'app-product-list',
standalone: true,
imports: [
AsyncPipe
],
templateUrl: './product-list.html'
})
export class ProductList {
private productService = inject(ProductService);
products$: Observable<Product[]> =
this.productService.getProducts();
}
product-list.html
<h2>Products</h2>
@if (products$ | async; as products) {
@for (product of products; track product.id) {
<div>
{{ product.name }} - ₹{{ product.price }}
</div>
}
}
app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideHttpClient } from '@angular/common/http';
export const appConfig: ApplicationConfig = {
providers: [
provideHttpClient()
]
};
3. How Does AsyncPipe Work?
We have:
products$: Observable<Product[]> =
this.productService.getProducts();
Notice that we're not doing:
.subscribe(...)
in the component.
Instead, the template does:
@if (products$ | async; as products) {
Angular's AsyncPipe subscribes for us.
Conceptually:
ProductService
│
▼
Observable<Product[]>
│
▼
products$
│
▼
AsyncPipe
│
├── subscribe()
│
▼
HTTP Request
│
▼
Product[]
│
▼
AsyncPipe
│
▼
Template
So:
products$ | async
essentially means:
Subscribe to
products$and give the template its latest emitted value.
4. Without AsyncPipe
Traditionally, you might write:
products: Product[] = [];
ngOnInit(): void {
this.productService
.getProducts()
.subscribe(products => {
this.products = products;
});
}
Template:
@for (product of products; track product.id) {
<p>
{{ product.name }}
</p>
}
Here the component manually subscribes.
Observable
│
▼
subscribe()
│
▼
products property
│
▼
Template
5. With AsyncPipe
Component:
products$ =
this.productService.getProducts();
Template:
@if (products$ | async; as products) {
@for (product of products; track product.id) {
<p>
{{ product.name }}
</p>
}
}
Now:
Observable
│
▼
AsyncPipe
│
▼
Template
Much less manual subscription code.
6. Why Do We Use $ in products$?
This:
products$
is just a naming convention.
It tells developers:
This variable contains an Observable.
For example:
products: Product[];
usually means:
Actual Product array
while:
products$: Observable<Product[]>;
means:
Observable that emits Product arrays
The $ has no special TypeScript functionality.
7. Automatic Subscription
Suppose:
products$ =
this.productService.getProducts();
and template:
@if (products$ | async; as products) {
AsyncPipe subscribes automatically.
You don't need:
products$.subscribe(...)
in the component.
This reduces boilerplate code.
8. Automatic Unsubscription
This is one of the biggest advantages of AsyncPipe.
Consider a long-lived Observable:
timer$ = interval(1000);
Without AsyncPipe:
this.subscription =
this.timer$.subscribe(...);
you need to manage the subscription lifecycle appropriately.
With:
<p>
{{ timer$ | async }}
</p>
Angular manages the subscription.
When the view is destroyed:
Component/View created
│
▼
AsyncPipe subscribes
│
▼
Observable emits
│
▼
Template updates
│
▼
View destroyed
│
▼
AsyncPipe unsubscribes
This helps prevent subscription leaks from long-lived streams.
9. AsyncPipe Always Shows the Latest Value
Consider:
counter$ = interval(1000);
Template:
<p>
Counter: {{ counter$ | async }}
</p>
The Observable emits:
0
1
2
3
4
...
The UI changes automatically:
Counter: 0
Counter: 1
Counter: 2
Counter: 3
So AsyncPipe keeps the template synchronized with the latest emitted value.
10. AsyncPipe with BehaviorSubject
This is another common Angular use case.
Service
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class UserService {
private userSubject =
new BehaviorSubject<string>('Guest');
user$ =
this.userSubject.asObservable();
setUser(name: string): void {
this.userSubject.next(name);
}
}
Component:
import { AsyncPipe } from '@angular/common';
import { Component, inject } from '@angular/core';
import { UserService } from './user.service';
@Component({
selector: 'app-user',
standalone: true,
imports: [AsyncPipe],
template: `
<h2>
Welcome {{ user$ | async }}
</h2>
`
})
export class User {
private userService = inject(UserService);
user$ = this.userService.user$;
}
Initially:
Welcome Guest
If:
this.userService.setUser('Syed');
the BehaviorSubject emits:
Syed
and AsyncPipe updates the template:
Welcome Syed
Flow:
BehaviorSubject
│
│ next("Syed")
▼
user$
│
▼
AsyncPipe
│
▼
Angular Template
│
▼
Welcome Syed
11. AsyncPipe with @if
A very useful modern Angular pattern is:
@if (user$ | async; as user) {
<h2>
Welcome {{ user.name }}
</h2>
}
The syntax:
as user
stores the emitted value in a template variable.
So:
user$
│
▼
async
│
▼
User object
│
▼
user
Then:
{{ user.name }}
can use that value.
12. AsyncPipe with @for
Suppose:
products$: Observable<Product[]> =
this.productService.getProducts();
Template:
@if (products$ | async; as products) {
@for (product of products; track product.id) {
<p>
{{ product.name }}
</p>
}
}
This is a clean pattern for displaying Observable data.
13. Why Use as products?
You might be tempted to write:
@if (products$ | async) {
@for (product of products$ | async; track product.id) {
{{ product.name }}
}
}
But this uses AsyncPipe multiple times on the same cold HTTP Observable expression/reference, which can result in multiple subscriptions—and with HttpClient, multiple subscriptions can mean multiple HTTP requests.
A cleaner approach is:
@if (products$ | async; as products) {
@for (product of products; track product.id) {
{{ product.name }}
}
}
Now the template uses the emitted result through:
products
rather than repeatedly applying async.
14. AsyncPipe and Change Detection
When an Observable emits a new value, AsyncPipe updates its latest value and marks the associated view for checking.
This is particularly useful with Angular's optimized change-detection patterns.
Conceptually:
Observable emits
│
▼
AsyncPipe receives
│
▼
Latest value updated
│
▼
View marked for checking
│
▼
Template reflects new value
15. AsyncPipe with Promise
Despite its name, AsyncPipe isn't limited to Observables.
It can also work with Promises.
For example:
messagePromise =
Promise.resolve('Hello Angular');
Template:
<p>
{{ messagePromise | async }}
</p>
Result:
Hello Angular
So:
AsyncPipe
│
├── Observable
│
└── Promise
However, in Angular/RxJS applications, you'll frequently encounter it with Observables.
16. Manual subscribe() vs AsyncPipe
Manual subscribe() |
AsyncPipe |
|---|---|
| Subscription in TypeScript | Subscription in template |
| You receive values in component code | Template receives latest value |
| Lifecycle management may be needed | Cleanup handled automatically |
| Useful for imperative side effects | Excellent for displaying data |
| More manual code | Less boilerplate |
17. When Should You Use AsyncPipe?
If the Observable's primary purpose is to provide data to the template, AsyncPipe is often an excellent choice.
For example:
products$ = this.productService.getProducts();
and:
@if (products$ | async; as products) {
...
}
This keeps the component reactive.
18. When Might You Use subscribe() Instead?
Sometimes you need an imperative side effect in TypeScript.
For example:
this.orderService
.createOrder(order)
.subscribe({
next: result => {
this.router.navigate(['/success']);
},
error: error => {
console.error(error);
}
});
Here you aren't simply displaying a value.
You want to perform an action:
Create order
│
▼
Success
│
▼
Navigate
Manual subscription can make sense for such imperative workflows.
So don't think:
AsyncPipe = always good
subscribe = always bad
Instead:
Display reactive data
↓
AsyncPipe
Imperative side effect
↓
subscribe() / appropriate effect handling
19. Standalone Angular Import
Because you're using standalone components, import:
import { AsyncPipe } from '@angular/common';
Then:
@Component({
standalone: true,
imports: [
AsyncPipe
]
})
Alternatively, importing CommonModule also makes common pipes/directives available, but importing AsyncPipe directly is more focused when that's all you need.
20. Easy Way to Remember
Without AsyncPipe:
Observable
│
▼
subscribe()
│
▼
Component property
│
▼
Template
With AsyncPipe:
Observable
│
▼
AsyncPipe
│
├── Subscribe
├── Get latest value
├── Update view
└── Unsubscribe automatically
│
▼
Template
Therefore:
AsyncPipeis Angular's template-friendly way to consume asynchronous Observable/Promise values.
Key Points
AsyncPipeis a built-in Angular pipe.- It works with Observables and Promises.
- It automatically subscribes to an Observable.
- It returns the latest emitted value to the template.
- It updates the view when a new value arrives.
- It automatically unsubscribes when appropriate, such as when the view is destroyed.
- It reduces manual subscription-management code.
- It is especially useful when Observable data is directly displayed in a template.
- In standalone components, import
AsyncPipefrom@angular/common. @if (observable$ | async; as value)is a useful modern Angular pattern.- Avoid unnecessary repeated
asyncusage on the same cold HTTP stream because multiple subscriptions can cause multiple requests. - Manual
subscribe()is still useful when you need imperative side effects rather than simply rendering Observable data.
Interview Questions and Answers
1. What is AsyncPipe?
Answer:AsyncPipe is a built-in Angular pipe that subscribes to an Observable or Promise, returns its latest value to the template, and manages the Observable subscription lifecycle automatically.
2. What is the syntax?
{{ observable$ | async }}
A common modern Angular pattern is:
@if (products$ | async; as products) {
@for (product of products; track product.id) {
{{ product.name }}
}
}
3. Does AsyncPipe subscribe automatically?
Answer:
Yes. When used with an Observable, AsyncPipe subscribes automatically.
You don't need to manually call:
.subscribe(...)
just to display the emitted value.
4. Does AsyncPipe unsubscribe automatically?
Answer:
Yes. Angular manages the subscription and unsubscribes when appropriate, such as when the containing view is destroyed.
5. Can AsyncPipe work with Promise?
Answer:
Yes.
It supports both:
Observable
Promise
6. Why is AsyncPipe useful?
Answer:
It reduces manual subscription code, automatically provides the latest asynchronous value to the template, triggers the necessary view update, and manages subscription cleanup.
7. subscribe() vs AsyncPipe?
Answer:
subscribe()
→ Explicit subscription in TypeScript
→ Useful for imperative side effects
AsyncPipe
→ Subscription managed by Angular
→ Excellent for displaying reactive data
8. How do you use AsyncPipe in a standalone component?
import { AsyncPipe } from '@angular/common';
@Component({
standalone: true,
imports: [AsyncPipe]
})
export class ProductList {
}
9. Why do we commonly use $ with AsyncPipe variables?
Answer:
The $ is a naming convention indicating that the variable contains an Observable.
products$: Observable<Product[]>;
It has no special TypeScript meaning.
10. Why should we avoid applying async repeatedly to the same HTTP Observable?
Answer:
Each AsyncPipe usage can create a subscription. Since Angular HttpClient Observables are cold, multiple subscriptions can trigger multiple HTTP requests.
A cleaner pattern is:
@if (products$ | async; as products) {
@for (product of products; track product.id) {
{{ product.name }}
}
}
11. Is AsyncPipe only for HTTP requests?
Answer:
No. It can consume many Observable sources, such as:
HttpClient
BehaviorSubject
Form/derived streams
Timers
Application state streams
and it can also consume Promises.
12. Short Interview Answer
AsyncPipeis an Angular built-in pipe that subscribes to an Observable or Promise, exposes its latest value to the template, updates the view when new values arrive, and automatically manages the Observable subscription lifecycle.