Using the Async Pipe in Angular
Aug 23, 2019 • 6 Minute Read
Introduction
The async pipe in angular will subscribe to an Observable or Promise and return the latest value it has emitted. Whenever a new value is emitted from an Observable or Promise, the async pipe marks the component to be checked for changes. When the component gets destroyed, the async pipe unsubscribes automatically to avoid potential memory leaks. We can use the async pipe in Angular application by including the CommonModule which exports all the basic Angular directives and pipes, such as NgIf, NgForOf, DecimalPipe, and so on. These are then re-exported by BrowserModule, which is included automatically in the root AppModule, when you create a new app with the CLI new command.
Syntax of AsyncPipe
The below syntax shows how we can use the async pipe with the object expression.
{{obj_expression | async}}
Using AsyncPipe with Promises
Async pipe for promises automatically adds a then callback and renders the response. Now let’s see an example where we are binding a Promise to the view. Clicking the Resolve button resolves the promise.
@Component({
selector: 'async -pipe',
template: `<div>
<code>promise|async</code>:
<button (click)="clicked()">{{ arrived ? 'Reset' : 'Resolve' }}</button>
<span>Wait for it... {{ logMessage | async }}</span>
</div>`
})
export class AsyncPipeComponent {
logMessage: Promise<string>|null = null;
arrived: boolean = false;
private resolve: Function|null = null;
constructor() { this.reset(); }
reset() {
this.arrived = false;
this.logMessage = new Promise<string>((resolve, reject) => { this.resolve = resolve; });
}
clicked() {
if (this.arrived) {
this.reset();
} else {
this.resolve !('hi there!');
this.arrived = true;
}
}
}
In the above example, we pipe the output of our promise to the async pipe. The property promise is the actual unresolved promise that gets returned from logMessage without then being called on it.
Using AsyncPipe with Observables
AsyncPipes for Observables automatically subscribes to the observable, renders the output, and then also unsubscribes when the component is destroyed. So, we don’t need to handle the cleanup logic ourselves. There is no need to unsubscribe manually in the component. Angular handles subscriptions of async pipes for us automatically using ngOnDestroy. AsyncPipe uses the OnPush change detection out of the box. We need to make sure that all our business logic is immutable and always returns new objects. We can say that the OnPush change detection strategy is great for performance so we should be using an async pipe as much as possible. Moreover, we could argue that the consistent use of these features simplifies application because developers can always assume one-way data flow and automatic subscription management. We can also use an async pipe with Observables. Now let’s see the example below that binds the time Observable to the view. The Observable continuously updates the view with the current time.
@Component({
selector: 'async -pipe-observable',
template: '<div><code>observable|async</code>: Time: {{ time | async }}</div>'
})
export class AsyncPipeObservableComponent {
time = new Observable<string>((observer: Observer<string>) => {
setInterval(() => observer.next(new Date().toString()), 1000);
});
}
In the above example, when we pipe our observable directly to the async pipe, the async pipe performs a subscription for us and then returns whatever gets passed to it. Advantages of using the async pipe are:
- We don’t need to call subscribe on our observable and store the intermediate data on our component.
- We don’t need to remember to unsubscribe from the observable when the component is destroyed.
Advantages of Using AsyncPipe over Subscribe
- AsyncPipe uses the OnPush change detection out of the box. We need to make sure that all our business logic is immutable and always returns new objects.
- Using subscribe() introduces a complementary need to unsubscribe at the end of the component life-cycle to avoid memory leaks. Developers have to unsubscribe manually. The most RxJS (declarative) common way to do this is to using takeUntil(unsubscribe$).
- There is no need to unsubscribe manually in the component. Angular handles subscriptions of async pipes for us automatically using ngOnDestroy.
- Using subscribe unwraps property to be used in multiple places in the template “as it is” without the need to rely on various workarounds.
- The Unwrapped property is available everywhere in the component. This means it can be used directly in the component’s method without the need to pass it from the template. That way, all of the states can be kept in the component.
- The OnPush change detection strategy is great for performance, so we should be using async pipe as much as possible. More so, we could argue that the consistent use of these features simplifies applications because developers can always assume one-way data flow and automatic subscription management.
Let’s consider an example below where we are using the async pipe in the EmployeesComponent class without using the subscribe. And there is no need to use the unwrapped property to use in the component and the template.
@Component({
template: `
<ul *ngIf="(employees$ | async).length">
<li *ngFor="let employee of employees$ | async">{{employee.name}}</li>
</ul>
`
})
export class EmployeesComponent implements OnInit {
employees$: Observable<Employee[]>;
constructor(private dept: Department<State>) {}
ngOnInit() {
this. employees$ = this.dept.pipe(select(selectEmployees))
}
}
Conclusion
In this guide, we have explored AsyncPipe in Angular. We have also seen how we can use AsyncPipe in our application using Promises or Observables.