取消订阅RxJS

以下代码适用于运行时转换,但有一件事困扰我 . 似乎 MyComponent 取消订阅并不像我预期的那样有效 . 当我在 MyComponent 和另一个之间来回导航,然后调用 changeLang() 时,控制台将输出 translated <key>result <key> ,就像我访问 MyComponent 一样多次 .

MyComponent 被销毁时,如何取消订阅 notify$

@Injectable({
    providedIn: 'root'
})
export class I18nService {
    lang = new ReplaySubject(1);
    notify$ = new ReplaySubject(1);

    constructor() {
        this.lang.pipe(
            startWith('en'),
            distinctUntilChanged(),
        ).subscribe(lang => {
            // load translations...

            this.notify$.next();
        })
    }

    changeLang(lang: Language) {
        this.lang.next(lang);
    }

    translate(key: string, interpolationOptions: InterpolationOptions): Observable<string> {
        return this.notify$.pipe(
            tap(() => console.log(`translated ${key}`)),
            map(() => this.getTranslationByKey(key, interpolationOptions)))
    }

    private getTranslationByKey(key: string, interpolationOptions: InterpolationOptions): string {
        // code omitted
    }
}

@Component({...})
export class MyComponent implements OnInit, OnDestroy {
    private subscription: Subscription;

    ngOnInit() {
        this.subscription = this.i18nService.translate('foo')
            .subscribe(result => console.log(`result ${result}`);
    }

    ngOnDestroy() {
        this.subscription.unsubscribe();
    }
}

回答(1)

2 years ago

你的代码工作得很好,并做了它应该做的事情 . 但正如Martin已经提到的那样,当您使用ReplaySubject时,在 ngOnInit() 订阅时将再次发出最后一个发射值 . 如果您不希望出现这种情况,请查看BehaviourSubject,here是一篇很好的文章,解释了这两者(及其差异) .

取消订阅时,这是一种巧妙的方法(特别是如果您有多个订阅要在组件中进行管理而又不想跟踪所有订阅):

private _onComponentDestroy = new Subject<void>();

ngOnInit() {
    this.i18nService.translate('foo')
        .pipe(takeUntil(this._onComponentDestroy))
        .subscribe(result => console.log(`result ${result}`);
}   

ngOnDestroy() {
  this._onComponentDestroy.next();
  this._onComponentDestroy.complete();
}

一旦组件被销毁,您将使用 pipe(takeUntil(this._onComponentDestroy)) 订阅,这将自动取消订阅所有Observable .