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

冷暖自知 提交于 2019-12-17 17:37:35

问题


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?


回答1:


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
}



回答2:


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.




回答3:


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.




回答4:


Solution provided above by @Hinrich is very good however sometimes you are blocked because you want to use multiple observables in the template, in this case there is a simple solution like that (which work well for hot observables like a ngrx selector but maybe not well for a cold observable like http requests) :

    @Component({
    selector: "some-comp",
    template: `
      <ng-container *ngIf="{ book: squareData$ | async, user: otherData$ | async} as data">
        Sub1: {{data.squareData}}<br>
        Sub2: {{data.otherData}}<br>
      </ng-container>`
})


来源:https://stackoverflow.com/questions/40742181/performance-of-using-same-observable-in-multiple-places-in-template-with-async-p

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!