← Back to Articles         
What is AsyncPipe?

What is AsyncPipe?

Published on 01 Oct 2026     10 min read Angular
RxJS & Observables

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:

AsyncPipe is Angular's template-friendly way to consume asynchronous Observable/Promise values.


Key Points

  • AsyncPipe is 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 AsyncPipe from @angular/common.
  • @if (observable$ | async; as value) is a useful modern Angular pattern.
  • Avoid unnecessary repeated async usage 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

AsyncPipe is 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.

← Back to Articles