如何在 Angular 9 中使用复杂的 RxJS 管道避免内存泄漏?
How to avoid memory leaks with complex RxJS pipes in Angular 9?
我的 Angular + RxJS 应用程序中有这个有点复杂的管道结构:
远程-data.service.ts:
getAll(url): Observable<any[]> {
return this.http.get(url, this.options)
.pipe(map((result: any[]) => result));
}
proxy.service.ts:
// localDataService use my LocalDataService
// remoteDataService use my RemoteDataService
getAll(model): Observable<any[]> {
if (model.use_localstorage) {
return this.localDataService.getAll(model)
.pipe(map((result: any[]) => result));
} else {
return this.remoteDataService.getAll(model.url)
.pipe(map((result: any[]) => result));
}
}
helper.service.ts:
getAll(model): Observable<any[]> {
// do some general fancy things...
return this.proxyService.getAll(model)
.pipe(map((result: any) => result));
}
}
然后在我的组件中:
export class MyComponent {
helperService = new HelperService();
model = new MyModel();
getAll() {
this.helperService.getAll().subscribe((result: any[]) => {
// parse result
});
}
}
如您所见,我从远程数据服务、代理服务、助手服务和组件构建了一个管道。当然,原因是将每个功能彼此分开,并使我的服务更具可重用性。
我的目标是避免内存泄漏。
问题是:如果我想将 take(1)
RxJS 运算符放入我的管道中,将其放入管道末尾, .subscribe()
之前或需要将其放入每个管道中就足够了服务和组件也是?
在这种情况下避免内存泄漏的最佳做法是什么?
视情况而定。 pipe(take(1))
确保你得到一个 "single Value observable" 的意思,它会发出一个值然后完成,并关闭订阅,因此没有内存泄漏。
如果您的服务或功能本身只发出一个单一值 pipe(take(1))
什么都不做。
例如,如果您有一个 REST 调用,httpclient.get(...)
它可能会有延迟。然后你应该使用 pipe(timeout(3000))
或 pipe(takeUntil(...))
来确保订阅没有活着,如果你的组件或任何东西被破坏你没有内存泄漏或订阅内部逻辑的意外行为。即使使用 pipe(take(1))
也可能存在内存泄漏,因为它只会在恰好发出一个值或错误后完成。
因此,如果您有一个 http.get()
并且它有网络延迟。你会使用 "pipe(take(1))" 它仍然会导致内存泄漏,因为它只等待一个值,并且当值到达时将触发订阅,即使你进行调用的组件被破坏或者你有导航到您的应用程序的另一个视图。
takeUntil(...) 对组件很有用,如果它们被销毁,您可以在 ngDestroy()
.
中触发订阅结束
public isActive = new Subject();
public ngOnDestroy(): void {
this.isActive.next(false);
}
public fun():void{
this.fooService.getValue()
.pipe(takeUntil(this.isActive))
.subscribe( value => console.log(value));
}
如果 Observable/Subject 完成,在发出最后一个值后应该没有内存泄漏。
你需要处理这些情况,如果你不确定,有一个 "complete()" 或者如果它只发出一个值但延迟可能会有问题。
内存泄漏是次要问题,甚至没有那么大。更多的问题是,当您不再想要时,您的订阅逻辑可能会被触发。
有时您想要触发订阅中的逻辑(例如通知用户某事已成功),即使用户已经离开视图。
所以这取决于。
您也可以将所有订阅存储在单个订阅中,如果您想处理它们,则取消订阅。
public subs = new Subscription();
public ngOnDestroy(): void {
this.subs.unsubscribe();
}
public fun():void{
const bar: Subscription = this.fooService.getValue()
.subscribe( value => console.log(value));
this.subs.add(bar);
}
有一个共同的模式 - takeUntil(this.destroy$)
应该是 .subscribe
调用之前任何管道中的最新运算符。
class MyClass {
destroy$ = new Subject();
ngOnInit() {
this.stream$.pipe(
takeUntil(this.destroy$),
).subscribe(data => {
// some logic.
});
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
}
对所有问题以及如何解决这些问题以避免内存泄漏有一个非常好的解释:https://blog.bitsrc.io/6-ways-to-unsubscribe-from-observables-in-angular-ab912819a78f
我的 Angular + RxJS 应用程序中有这个有点复杂的管道结构:
远程-data.service.ts:
getAll(url): Observable<any[]> {
return this.http.get(url, this.options)
.pipe(map((result: any[]) => result));
}
proxy.service.ts:
// localDataService use my LocalDataService
// remoteDataService use my RemoteDataService
getAll(model): Observable<any[]> {
if (model.use_localstorage) {
return this.localDataService.getAll(model)
.pipe(map((result: any[]) => result));
} else {
return this.remoteDataService.getAll(model.url)
.pipe(map((result: any[]) => result));
}
}
helper.service.ts:
getAll(model): Observable<any[]> {
// do some general fancy things...
return this.proxyService.getAll(model)
.pipe(map((result: any) => result));
}
}
然后在我的组件中:
export class MyComponent {
helperService = new HelperService();
model = new MyModel();
getAll() {
this.helperService.getAll().subscribe((result: any[]) => {
// parse result
});
}
}
如您所见,我从远程数据服务、代理服务、助手服务和组件构建了一个管道。当然,原因是将每个功能彼此分开,并使我的服务更具可重用性。
我的目标是避免内存泄漏。
问题是:如果我想将 take(1)
RxJS 运算符放入我的管道中,将其放入管道末尾, .subscribe()
之前或需要将其放入每个管道中就足够了服务和组件也是?
在这种情况下避免内存泄漏的最佳做法是什么?
视情况而定。 pipe(take(1))
确保你得到一个 "single Value observable" 的意思,它会发出一个值然后完成,并关闭订阅,因此没有内存泄漏。
如果您的服务或功能本身只发出一个单一值 pipe(take(1))
什么都不做。
例如,如果您有一个 REST 调用,httpclient.get(...)
它可能会有延迟。然后你应该使用 pipe(timeout(3000))
或 pipe(takeUntil(...))
来确保订阅没有活着,如果你的组件或任何东西被破坏你没有内存泄漏或订阅内部逻辑的意外行为。即使使用 pipe(take(1))
也可能存在内存泄漏,因为它只会在恰好发出一个值或错误后完成。
因此,如果您有一个 http.get()
并且它有网络延迟。你会使用 "pipe(take(1))" 它仍然会导致内存泄漏,因为它只等待一个值,并且当值到达时将触发订阅,即使你进行调用的组件被破坏或者你有导航到您的应用程序的另一个视图。
takeUntil(...) 对组件很有用,如果它们被销毁,您可以在 ngDestroy()
.
public isActive = new Subject();
public ngOnDestroy(): void {
this.isActive.next(false);
}
public fun():void{
this.fooService.getValue()
.pipe(takeUntil(this.isActive))
.subscribe( value => console.log(value));
}
如果 Observable/Subject 完成,在发出最后一个值后应该没有内存泄漏。
你需要处理这些情况,如果你不确定,有一个 "complete()" 或者如果它只发出一个值但延迟可能会有问题。
内存泄漏是次要问题,甚至没有那么大。更多的问题是,当您不再想要时,您的订阅逻辑可能会被触发。 有时您想要触发订阅中的逻辑(例如通知用户某事已成功),即使用户已经离开视图。 所以这取决于。
您也可以将所有订阅存储在单个订阅中,如果您想处理它们,则取消订阅。
public subs = new Subscription();
public ngOnDestroy(): void {
this.subs.unsubscribe();
}
public fun():void{
const bar: Subscription = this.fooService.getValue()
.subscribe( value => console.log(value));
this.subs.add(bar);
}
有一个共同的模式 - takeUntil(this.destroy$)
应该是 .subscribe
调用之前任何管道中的最新运算符。
class MyClass {
destroy$ = new Subject();
ngOnInit() {
this.stream$.pipe(
takeUntil(this.destroy$),
).subscribe(data => {
// some logic.
});
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
}
对所有问题以及如何解决这些问题以避免内存泄漏有一个非常好的解释:https://blog.bitsrc.io/6-ways-to-unsubscribe-from-observables-in-angular-ab912819a78f