"Subscribing manually to an observable"
I somewhat disagree here. The async pipe will create a new subscription for each occurrence. In all but all but basic applications this is not ideal, you can end up with dozens of subscriptions to the same Observable, when if you subscribe manually and and set the property in the component, you will only need one. I've had massive performance increases on pages by being extremely careful about using the async pipe.
Additionally, as you pointed out, the async pipe does not handle errors. I have also run into very hard debug issues regarding component lifecycle and initialization of Observables when using the async pipe. I'd actually say avoid async pipes unless it's a really basic page or application. Instead, create a mixin which contains a disposer$ Subject that is called via ngOnDestroy and use the takeUntil(this.disposer$) on your observables in components. It's a clean pattern that avoids the performance hits and lost of functionality that come with the async pipe.
Example WithDisposableSubjectMixin
import { OnDestroy } from '@angular/core';
import { Constructor } from '@angular/material/core/common-behaviors/constructor';
import { Subject } from 'rxjs';
export function WithDisposableSubject<T extends Constructor<{}>>(Base: T = (class { } as any)) {
return class extends Base implements OnDestroy {
disposer$ = new Subject<any>();
ngOnDestroy() {
this.disposer$.next();
this.disposer$.complete();
}
};
}
👨🏫 Co-Founder of This is Learning, Organizer of AarhusJS
✍️ Writer, Speaker, FOSS Maintainer 📗 Author
🏆 Microsoft MVP 🌟 GitHub Star
🌊 Nx Champion 🦸 Angular Hero of Education
Thank you for your feedback. Personally, I use container components and presentational components. This solves the issue with multiple subscriptions since the container component subscribes to the observable exactly once. It also solves the issue you mention with observable subscription time since the presentational component is not aware of observable values.
👨🏫 Co-Founder of This is Learning, Organizer of AarhusJS
✍️ Writer, Speaker, FOSS Maintainer 📗 Author
🏆 Microsoft MVP 🌟 GitHub Star
🌊 Nx Champion 🦸 Angular Hero of Education
Your solution comes with a footgun. If the takeUntil operation is placed in the wrong position of the observable pipeline, it will lead to unforeseen consequences such as memory leaks and duplicate side effects.
Not to mention, using an async pipe inside of an *ngIf can end up causing a massive number of subscriptions to be created an disposed if an element is toggled frequently.
<strong *ngIf="visible">{{totalCount$ | async}} Selected</strong>
Every time that visible switches from true to false, a brand new subscription is created/disposed to $totalCount. This can have serious performance impacts on an application, not mention it's very easy to end up with this type of code triggering a ton of network requests if totalCount$ is hitting the server.
I'm sure someone has written up a good article on the dangers of the async pipe.
👨🏫 Co-Founder of This is Learning, Organizer of AarhusJS
✍️ Writer, Speaker, FOSS Maintainer 📗 Author
🏆 Microsoft MVP 🌟 GitHub Star
🌊 Nx Champion 🦸 Angular Hero of Education
I'm sure someone has written up a good article on the dangers of the async pipe.
I think AsyncPipe is grossly over-rated. Michael Hladky has talked a lot about what AsyncPipe is missing. He's even working on a range of replacements in the RxAngular (@rx-angular/*) packages.
Come to think of it, I interviewed him about this very topic.
For further actions, you may consider blocking this person and/or reporting abuse
We're a place where coders share, stay up-to-date and grow their careers.
"Subscribing manually to an observable"
I somewhat disagree here. The async pipe will create a new subscription for each occurrence. In all but all but basic applications this is not ideal, you can end up with dozens of subscriptions to the same Observable, when if you subscribe manually and and set the property in the component, you will only need one. I've had massive performance increases on pages by being extremely careful about using the async pipe.
Additionally, as you pointed out, the async pipe does not handle errors. I have also run into very hard debug issues regarding component lifecycle and initialization of Observables when using the async pipe. I'd actually say avoid async pipes unless it's a really basic page or application. Instead, create a mixin which contains a disposer$ Subject that is called via ngOnDestroy and use the
takeUntil(this.disposer$)
on your observables in components. It's a clean pattern that avoids the performance hits and lost of functionality that come with the async pipe.Example WithDisposableSubjectMixin
Then use in component
Hi Ray,
Thank you for your feedback. Personally, I use container components and presentational components. This solves the issue with multiple subscriptions since the container component subscribes to the observable exactly once. It also solves the issue you mention with observable subscription time since the presentational component is not aware of observable values.
Your solution comes with a footgun. If the
takeUntil
operation is placed in the wrong position of the observable pipeline, it will lead to unforeseen consequences such as memory leaks and duplicate side effects.Not to mention, using an async pipe inside of an *ngIf can end up causing a massive number of subscriptions to be created an disposed if an element is toggled frequently.
<strong *ngIf="visible">{{totalCount$ | async}} Selected</strong>
Every time that visible switches from true to false, a brand new subscription is created/disposed to
$totalCount
. This can have serious performance impacts on an application, not mention it's very easy to end up with this type of code triggering a ton of network requests iftotalCount$
is hitting the server.I'm sure someone has written up a good article on the dangers of the async pipe.
I think
AsyncPipe
is grossly over-rated. Michael Hladky has talked a lot about whatAsyncPipe
is missing. He's even working on a range of replacements in the RxAngular (@rx-angular/*
) packages.Come to think of it, I interviewed him about this very topic.