Angular/RxJs - 在组件取消订阅后调用服务中 Observable 的 next() 导致 ObjectUnsubscribedError
Angular/RxJs - Calling next() on Observable in service after component was unsubscribe() cause ObjectUnsubscribedError
我有一个定义 public Subject
的 Angular 服务。稍后在某些情况下,该服务将在此主题上调用 .next()
。
此外,我有一个 Angular 组件,在它的 ngOnInit()
方法中 .subscribe()
到这个主题,并在它的 ngOnDestroy()
方法中调用 .unsubscribe()
.
虽然服务和组件都处于活动状态,但我的代码工作正常。但是,用户可以单击我的应用程序中的其他选项卡。这导致我的组件卸载并调用其 ngOnDestroy()
方法,这意味着 Observable 已取消订阅。
从这一点开始,该组件不再存在,但该服务仍然存在 - 其他组件仍在使用它。当服务在组件调用 .unsubscribe()
之后调用 .next()
方法时,将抛出此错误 ObjectUnsubscribedError。
ERROR Error: Uncaught (in promise): ObjectUnsubscribedError: object unsubscribed
此外,如果用户稍后返回到包含该组件的选项卡,则会执行 ngOnInit()
并且重新订阅此可观察对象会再次引发相同的错误。
我做错了什么,我该如何解决这些错误?
import { Subject } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class StatusService {
public statusChange$: Subject<number>;
public constructor(...) {
this.statusChange$ = new Subject<number>();
}
public someInternalLogic(someNumber: number): void {
this.statusChange$.next(someNumber);
// this will throw an error if StatusComponent call .unsubscribe()
}
}
@Component({
selector: 'app-status',
templateUrl: './status.component.html',
styleUrls: ['./status.component.scss']
})
export class StatusComponent implements OnInit, OnDestroy {
public constructor(
private _statusService: StatusService) {}
public ngOnInit(): void {
this._statusService.statusChange$.subscribe(value => this._onStatusChange(value));
}
public ngOnDestroy(): void {
this._statusService.statusChange$.unsubscribe();
// after this point if the StatusService calling .next() an error will be thrown
}
private _onStatusChange(value: number): void {
// ....
}
}
您不需要取消订阅组件中的服务主题。而是在您的组件中为服务主题创建一个订阅变量,并在组件 destroy
上取消订阅
subscription!: Subscription;
public ngOnInit(): void {
this.subscription = this._statusService.statusChange$.subscribe(value => this._onStatusChange(value));
}
public ngOnDestroy(): void {
this.subscription.unsubscribe();
}
private _onStatusChange(value: number): void {
// ....
}
另一种集体取消订阅 OnDestroy 事件的方法是使用 takeUntil 运算符。因此,您不必存储每个订阅,而是可以创建一个主题并在组件被销毁时触发它。
假设您的服务有多个主题,并且您的组件订阅了每个主题。在这种情况下,您必须通过 class 属性(或与此相关的一系列订阅)存储所有订阅,并且您必须手动取消订阅每个订阅。
class myComponent {
// create any subject which allows you to push into
// when your component gets destroyed
private onDestroy = new Subject<void>();
public ngOnInit(): void {
// Subscription #1
this._statusService.statusChange_1$.pipe(
tap(() => /* do something */),
// takeUntil will listen to changes of the onDestroy subject
// and whenever you trigger it by this.onDestroy.next() it will
// automatically unsubsribe for you
takeUntil(this.onDestroy)
).subscribe();
// Subscription #2
this._statusService.statusChange_2$.pipe(
takeUntil(this.onDestroy)
).subscribe(value => {
/* do something else */
// you can put your logic into your subscribe method as well
// like you did in your question
});
// Any number of more subscriptions...
// ...
}
public ngOndestroy(): void {
// Notify the observers to unsibscribe
this.onDestroy.next();
}
}
此方法适用于任何可观察对象。
我有一个定义 public Subject
的 Angular 服务。稍后在某些情况下,该服务将在此主题上调用 .next()
。
此外,我有一个 Angular 组件,在它的 ngOnInit()
方法中 .subscribe()
到这个主题,并在它的 ngOnDestroy()
方法中调用 .unsubscribe()
.
虽然服务和组件都处于活动状态,但我的代码工作正常。但是,用户可以单击我的应用程序中的其他选项卡。这导致我的组件卸载并调用其 ngOnDestroy()
方法,这意味着 Observable 已取消订阅。
从这一点开始,该组件不再存在,但该服务仍然存在 - 其他组件仍在使用它。当服务在组件调用 .unsubscribe()
之后调用 .next()
方法时,将抛出此错误 ObjectUnsubscribedError。
ERROR Error: Uncaught (in promise): ObjectUnsubscribedError: object unsubscribed
此外,如果用户稍后返回到包含该组件的选项卡,则会执行 ngOnInit()
并且重新订阅此可观察对象会再次引发相同的错误。
我做错了什么,我该如何解决这些错误?
import { Subject } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class StatusService {
public statusChange$: Subject<number>;
public constructor(...) {
this.statusChange$ = new Subject<number>();
}
public someInternalLogic(someNumber: number): void {
this.statusChange$.next(someNumber);
// this will throw an error if StatusComponent call .unsubscribe()
}
}
@Component({
selector: 'app-status',
templateUrl: './status.component.html',
styleUrls: ['./status.component.scss']
})
export class StatusComponent implements OnInit, OnDestroy {
public constructor(
private _statusService: StatusService) {}
public ngOnInit(): void {
this._statusService.statusChange$.subscribe(value => this._onStatusChange(value));
}
public ngOnDestroy(): void {
this._statusService.statusChange$.unsubscribe();
// after this point if the StatusService calling .next() an error will be thrown
}
private _onStatusChange(value: number): void {
// ....
}
}
您不需要取消订阅组件中的服务主题。而是在您的组件中为服务主题创建一个订阅变量,并在组件 destroy
上取消订阅subscription!: Subscription;
public ngOnInit(): void {
this.subscription = this._statusService.statusChange$.subscribe(value => this._onStatusChange(value));
}
public ngOnDestroy(): void {
this.subscription.unsubscribe();
}
private _onStatusChange(value: number): void {
// ....
}
另一种集体取消订阅 OnDestroy 事件的方法是使用 takeUntil 运算符。因此,您不必存储每个订阅,而是可以创建一个主题并在组件被销毁时触发它。
假设您的服务有多个主题,并且您的组件订阅了每个主题。在这种情况下,您必须通过 class 属性(或与此相关的一系列订阅)存储所有订阅,并且您必须手动取消订阅每个订阅。
class myComponent {
// create any subject which allows you to push into
// when your component gets destroyed
private onDestroy = new Subject<void>();
public ngOnInit(): void {
// Subscription #1
this._statusService.statusChange_1$.pipe(
tap(() => /* do something */),
// takeUntil will listen to changes of the onDestroy subject
// and whenever you trigger it by this.onDestroy.next() it will
// automatically unsubsribe for you
takeUntil(this.onDestroy)
).subscribe();
// Subscription #2
this._statusService.statusChange_2$.pipe(
takeUntil(this.onDestroy)
).subscribe(value => {
/* do something else */
// you can put your logic into your subscribe method as well
// like you did in your question
});
// Any number of more subscriptions...
// ...
}
public ngOndestroy(): void {
// Notify the observers to unsibscribe
this.onDestroy.next();
}
}
此方法适用于任何可观察对象。