Performance of using same observable in multiple places in template with async pipe

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.


I had better luck with .shareReplay from 'rxjs/add/operator/shareReplay' which is very new (https://github.com/ReactiveX/rxjs/pull/2443)

I also had luck with .publishReplay.refCount(1) (Angular 2 + rxjs: async pipe with .share() operator)

I'm honestly not sure about the difference between the two strategies. The comments in the PR for shareReplay suggest that there might be more risk for memory leaks of Subscriptions if not implemented correctly, so I might go with the .publishReplay.refCount(1) for now.

Tags:

Angular

Rxjs