問題描述
使用 NgRx 存儲優化 Angular 內存以避免內存洩漏 (Angular memory optimization with NgRx store to avoid memory leaks)
我們還有許多其他可觀察的訂閱來監聽 App State 的不同部分。我有幾個疑問:</p>
- 訂閱
this.accountProgress$.subscribe(() => {...})
仍然會導致內存洩漏嗎?如果在同一個組件中多次使用它以不同的方法獲取數據?我們是否應該在這種訂閱中也使用takeUntil()
或take(1)
(我們在少數地方有這個,但我不確定它是否有幫助)?或者使用publish() + refCount()
或share({refCount: true})
和this.store.pipe(select( appState => appState.account.accountProgress))
? - 有時我們需要
dispatch
一個基於我們在訂閱中收到的值的操作,我知道這是一個 反模式,我們不應該dispatch
訂閱中的操作,但是可以做什麼,因為很少有商店依賴於 API 響應,並且不確定何時返回將決定業務邏輯的進一步執行的數據 - 還有其他優化方法嗎?(請注意,我們在組件中的任何地方都遵循上述
takeUntil()
語法,在處理 NgRx 存儲、效果和 Observable 時是否有任何其他方法來防止內存消耗)
dispatch
一個動作,但是可以做什麼,因為很少有商店依賴於 API 響應,而且它何時返回數據非常不確定將決定業務邏輯的進一步執行</li> takeUntil()
語法,在處理 NgRx 存儲、效果和 Observables 時是否有任何其他方法來防止內存消耗)dispatch
一個動作,但是可以做什麼,因為很少有商店依賴於 API 響應,而且它何時返回數據非常不確定將決定業務邏輯的進一步執行 takeUntil()
語法,在處理 NgRx 存儲、效果和 Observable 時是否有任何其他方法來防止內存消耗)takeUntil()
語法,在處理 NgRx 存儲、效果和 Observable 時是否有任何其他方法來防止內存消耗)takeUntil()
語法,在處理 NgRx 存儲、效果和 Observable 時是否有任何其他方法來防止內存消耗)</li > </ul> 參考解法
方法 1:
Your actual methods to handle subscriptions are great, but I think the best way to handle rxjs code in Angular is to use the async pipe and good data composition.
You have to think in all the data needed to render your component and combine it to get just one final observable to be subscribed in HTML, in that way you can take advantage of ChangeDetectionStrategy
and trackBy
for *ngFor
with reactive data.
Follow these links to get a better undertanding:
Hope I helped you a little.
方法 2:
Can the subscription
this.accountProgress$.subscribe(() => {...})
still cause a memory leak?
A while ago, I wrote here an answer that describes why memory leaks occur when using a Subject in RxJS. So, in this case I'd say it won't, because you're using takeUntil(this.destroyed$)
.
If it is used multiple times within the same component to fetch data in different methods?
If takeUntil
is still there, there won't be any memory leaks. But, there is an important aspect that you should be aware of.
Suppose you have a subject which has a few subscribers:
const s = new Subject();
const o1$ = s.pipe(
a(),
b(),
c(),
d(),
);
const o2$ = s.pipe(
a(),
b(),
);
If you subscribe to o1$
3 times, there will be 3 distinct instances of each operator used until s.pipe
, namely a
, b
, c
, d
. This might be something you'd want to avoid and you can do so by using share()
, shareReplay()
operators:
const o1$ = s.pipe(
a(),
b(),
c(),
d(),
share(),
);
// now, when `o1$` is subscribed, each of the `a‑d` operators will have exactly one instance
o1$.subscribe()
o1$.subscribe()
o1$.subscribe()
Should we use
takeUntil()
ortake(1)
with this kind of subscriptions as well
It depends, take(n)
will complete after the n‑th
values has been emitted and takeUntil(notifier$)
will complete when notifier$
emits.
Sometimes we need to dispatch an action based on the value we receive within the subscription ...
Maybe instead of doing this in the subscription, you can use the tap
operator, which is well‑known for side effects.
data$ = this.store.select(yourSelector).pipe(
/* ... */
tap(result => this.store.dispatch(anAction))
/* ... */
)
Also, the data$
observable from above can be used in conjunction with the async pipe, so you don't have to deal with manual subscription/unsubscription.
(by patrick.1729、Gustavo Alarcón、Andrei Gătej)