In my component template I am calling async
pipe for same Observable in 2 places.
Shall I subscribe to it and use returned array in my template or using async
pipe for same Observable in multiple places of template has no negative effect to performence?
The AsyncPipe is quite the handy little tool. And I think it's reasonable to say that a well-designed Angular application should never need to subscribe to an Observable. The async pipe can take care of subscribing and unsubscribing while we worry about more important stuff.
Observables are "lazy", meaning if no one is listening, nothing happens.
An observable produces values over time. An array is created as a static set of values. In a sense, observables are asynchronous where arrays are synchronous. In the following examples, → implies asynchronous value delivery.
Async pipelinkThe AsyncPipe subscribes to an observable or promise and returns the latest value it has emitted.
Every use of observable$ | async
will create a new subscription(and therefor an individual stream) to the given observable$
- if this observable contains parts with heavy calculations or rest-calls, those calculations and rest-calls are executed individually for each async
- so yes - this can have performance implications.
However this is easily fixed by extending your observable$
with .share()
, to have a shared stream among all subscribers and execute all those things just once for all subscribers. Don't forget to add the share
-operator with import "rxjs/add/operator/share";
The reason why async-pipes don't share subscriptions by default is simply flexibility and ease of use: A simple .share()
is much faster to write than creating a completely new stream, which would be required if they were to be shared by default.
Here is a quick example
@Component({ selector: "some-comp", template: ` Sub1: {{squareData$ | async}}<br> Sub2: {{squareData$ | async}}<br> Sub3: {{squareData$ | async}} ` }) export class SomeComponent { squareData$: Observable<string> = Observable.range(0, 10) .map(x => x * x) .do(x => console.log(`CalculationResult: ${x}`) .toArray() .map(squares => squares.join(", ")) .share(); // remove this line and the console will log every result 3 times instead of 1 }
Another way of avoiding multiple subscriptions is to use a wrapping *ngIf="obs$ | async as someName"
. Using olsn's example
@Component({ selector: "some-comp", template: ` <ng-container *ngIf="squareData$ | async as squareData"> Sub1: {{squareData}}<br> Sub2: {{squareData}}<br> Sub3: {{squareData}} </ng-container>` }) export class SomeComponent { squareData$: Observable<string> = Observable.range(0, 10) .map(x => x * x) .do(x => console.log(`CalculationResult: ${x}`) .toArray() .map(squares => squares.join(", ")); }
It's also cool because it cleans out the template a bit too.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With