提问者:小点点

RxJS:带直到()角度组件的ngOnDestroy()


tl; dr:基本上我想将Angular的ngOnDestroy与RxjstokTo()运算符结合起来。--这可能吗?

我有一个Angular组件,它打开了几个Rxjs订阅。当组件被销毁时,这些需要关闭。

一个简单的解决办法是:

class myComponent {

  private subscriptionA;
  private subscriptionB;
  private subscriptionC;

  constructor(
    private serviceA: ServiceA,
    private serviceB: ServiceB,
    private serviceC: ServiceC) {}

  ngOnInit() {
    this.subscriptionA = this.serviceA.subscribe(...);
    this.subscriptionB = this.serviceB.subscribe(...);
    this.subscriptionC = this.serviceC.subscribe(...);
  }

  ngOnDestroy() {
    this.subscriptionA.unsubscribe();
    this.subscriptionB.unsubscribe();
    this.subscriptionC.unsubscribe();
  }

}

这是可行的,但它有点多余。我特别不喜欢这样-取消订阅()在其他地方,所以你必须记住这些是链接的。-组件状态被订阅污染了。

我更喜欢使用tokIt()运算符或类似的东西,使其看起来像这样:

class myComponent {

  constructor(
    private serviceA: ServiceA,
    private serviceB: ServiceB,
    private serviceC: ServiceC) {}

  ngOnInit() {
    const destroy = Observable.fromEvent(???).first();
    this.subscriptionA = this.serviceA.subscribe(...).takeUntil(destroy);
    this.subscriptionB = this.serviceB.subscribe(...).takeUntil(destroy);
    this.subscriptionC = this.serviceC.subscribe(...).takeUntil(destroy);
  }

}

是否有一个销毁事件或类似的东西可以让我使用tototo()或其他类似的方法来简化组件架构?我意识到我可以在构造函数中自己创建一个事件或在ngOnDestroy()中触发的东西,但这最终不会使事情变得更容易阅读。


共3个答案

匿名用户

您可以利用ReplaySub来实现:

编辑:与RxJS 6. x不同:请注意tube()方法的使用。

class myComponent {
  private destroyed$: ReplaySubject<boolean> = new ReplaySubject(1);

  constructor(
    private serviceA: ServiceA,
    private serviceB: ServiceB,
    private serviceC: ServiceC) {}

  ngOnInit() {
    this.serviceA
      .pipe(takeUntil(this.destroyed$))
      .subscribe(...);
    this.serviceB
      .pipe(takeUntil(this.destroyed$))
      .subscribe(...);
    this.serviceC
      .pipe(takeUntil(this.destroyed$))
      .subscribe(...);
  }

  ngOnDestroy() {
    this.destroyed$.next(true);
    this.destroyed$.complete();
  }
}

这仅对RxJS 5. x及更早版本有效:

class myComponentOld {
  private destroyed$: ReplaySubject<boolean> = new ReplaySubject(1);

  constructor(private serviceA: ServiceA) {}

  ngOnInit() {
    this.serviceA
      .takeUntil(this.destroyed$)
      .subscribe(...);
  }

  ngOnDestroy() {
    this.destroyed$.next(true);
    this.destroyed$.complete();
  }
}

匿名用户

使用npm包@w11k/ngx-组件销毁中的组件销毁()函数是迄今为止最简单的使用带直到的方法:

@Component({
  selector: 'foo',
  templateUrl: './foo.component.html'
})
export class FooComponent implements OnInit, OnDestroy {
  ngOnInit() {
    Observable.interval(1000)
      .takeUntil(componentDestroyed(this)) // <--- magic is here!
      .subscribe(console.log);
  }

  ngOnDestroy() {}
}

这是直接包含在您的代码中的组件Destroed()版本:

// Based on https://www.npmjs.com/package/ng2-rx-componentdestroyed
import { OnDestroy } from '@angular/core';
import { ReplaySubject } from 'rxjs/ReplaySubject';

export function componentDestroyed(component: OnDestroy) {
  const oldNgOnDestroy = component.ngOnDestroy;
  const destroyed$ = new ReplaySubject<void>(1);
  component.ngOnDestroy = () => {
    oldNgOnDestroy.apply(component);
    destroyed$.next(undefined);
    destroyed$.complete();
  };
  return destroyed$;
}

匿名用户

好吧,这归结为您关闭订阅的意思。基本上有两种方法可以做到这一点:

  1. 使用完成链的运算符(例如tochAnd())。
  2. 取消订阅源可观察。

很高兴知道这两个不一样。

当使用例如的时()您使操作员发送完整的通知,该通知将传播给您的观察者。所以如果你定义:

...
.subscribe(..., ..., () => doWhatever());

然后,当您使用例如完成链时。getAfter()将调用doWhath()函数。

例如,它可能看起来像这样:

const Observable = Rx.Observable;
const Subject = Rx.Subject;

let source = Observable.timer(0, 1000);
let subject = new Subject();

source.takeUntil(subject).subscribe(null, null, () => console.log('complete 1'));
source.takeUntil(subject).subscribe(null, null, () => console.log('complete 2'));
source.takeUntil(subject).subscribe(null, null, () => console.log('complete 3'));

setTimeout(() => {
  subject.next();
}, 3000);

3s后将调用所有完整的回调。

另一方面,当你取消订阅时,你说你不再对源可观察生成的项目感兴趣。然而,这并不意味着源必须完成。你只是不在乎了。

这意味着您可以从收集所有Subscription。订阅(…)调用并一次取消订阅所有这些调用:

let subscriptions = new Rx.Subscription();
let source = Observable.timer(0, 1000);

subscriptions.add(source.subscribe(null, null, () => console.log('complete 1')));
subscriptions.add(source.subscribe(null, null, () => console.log('complete 2')));
subscriptions.add(source.subscribe(null, null, () => console.log('complete 3')));

setTimeout(() => {
  subscriptions.unsubscribe();
}, 3000);

现在,在3秒延迟之后,不会将任何内容打印到控制台,因为我们取消了订阅并且没有调用完整的回调。

所以你想使用什么取决于你和你的用例。请注意,取消订阅并不等同于完成,尽管我想在你的情况下这并不重要。