Angular RxJS:takeUntil()角度分量';s Ngondestory()

Angular RxJS:takeUntil()角度分量';s Ngondestory(),angular,components,rxjs,rxjs5,Angular,Components,Rxjs,Rxjs5,tl;dr:基本上我想把Angular的Ngondestory和RxjstakeUntil()操作符结合起来可能吗 我有一个角度组件,可以打开几个Rxjs订阅。 当组件被销毁时,需要将其关闭 一个简单的解决方案是: class myComponent { private subscriptionA; private subscriptionB; private subscriptionC; constructor( private serviceA: ServiceA

tl;dr:基本上我想把Angular的
Ngondestory
和Rxjs
takeUntil()
操作符结合起来可能吗

我有一个角度组件,可以打开几个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();
  }

}
这是可行的,但有点多余。我尤其不喜欢这样 -取消订阅()的
unsubscribe()
在其他地方,因此您必须记住它们是链接的。 -组件状态被订阅污染

我更喜欢使用
takeUntil()
操作符或类似的方法,使其看起来像这样:

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);
  }

}
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);
是否存在销毁事件或类似事件,允许我使用
takeUntil()
或其他类似方法来简化组件体系结构?
我意识到我可以自己在构造函数中创建一个事件,或者在
ngondstroy()
中触发某个事件,但这最终不会使阅读变得简单。

您可以利用
ReplaySubject
实现这一点:

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

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();
  }
}
类myComponent{
private destromed$:ReplaySubject=新ReplaySubject(1);
建造师(
私人服务a:serviceA,
私人服务b:serviceB,
私人服务c:serviceC{}
恩戈尼尼特(){
这是服务
.pipe(takeUntil(此.destromed$))
.认购(……);
这是服务B
.pipe(takeUntil(此.destromed$))
.认购(……);
这是我的服务
.pipe(takeUntil(此.destromed$))
.认购(……);
}
恩贡德斯特罗(){
此.next(true)已销毁$;
此.complete$.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();
  }
}
类myComponentOld{
private destromed$:ReplaySubject=新ReplaySubject(1);
构造函数(私有serviceA:serviceA){}
恩戈尼尼特(){
这是服务
.takeUntil(此.destromed$)
.认购(……);
}
恩贡德斯特罗(){
此.next(true)已销毁$;
此.complete$.complete();
}
}

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

  • 使用完成链的运算符(例如
    takeWhile()
  • 从可观察的源取消订阅
  • 很高兴知道这两个不一样

    例如,当使用
    takeWhile()
    时,您让操作员发送
    complete
    通知,并将其传播给您的观察者。因此,如果您定义:

    ...
    .subscribe(..., ..., () => doWhatever());
    
    然后,当您使用例如
    takeWhile()
    完成链时,将调用
    doWhatever()
    函数

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

    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);
      }
    
    }
    
    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后,将调用所有完整的回调

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

    这意味着您可以从
    .subscribe(…)
    呼叫中收集所有
    订阅
    并立即取消订阅:

    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);
    
    现在,在3s延迟之后,不会向控制台打印任何内容,因为我们取消了订阅,并且没有调用任何完整的回调


    因此,您想要使用什么取决于您和您的用例。请注意,取消订阅与完成并不相同,尽管我猜在您的情况下这并不重要。

    使用npm软件包中的
    componentdestromed()
    函数是目前为止最简单的使用方法,直到:

    @组件({
    选择器:“foo”,
    templateUrl:“./foo.component.html”
    })
    导出类FooComponent实现OnInit、OnDestroy{
    恩戈尼尼特(){
    可观测间隔(1000)
    
    .takeUntil(componentDestroyed(this))/如果您的组件直接绑定到路由,您可以通过利用避免添加状态。这样,一旦您离开该组件,它将自动为您清理其订阅

    import { Component, OnInit } from '@angular/core';
    import { ActivatedRoute, Router } from '@angular/router';
    import { MyService } from './my.service';
    import { Observable } from 'rxjs/Observable';
    import 'rxjs/add/operator/takeUntil';
    
    @Component({
        ...
    })
    export class ExampleComopnent implements OnInit {
    
        constructor(private router: Router, private myService: MyService) {
        }
    
        ngOnInit() {
            this.myService.methodA()
                .takeUntil(this.router.events)
                .subscribe(dataA => {
                    ...
                });
    
            this.myService.methodB()
                .takeUntil(this.router.events)
                .subscribe(dataB => {
                    ...
                });
        }
    }
    
    注意:这个简单的示例不考虑受保护的路由或取消的路由导航。如果有可能触发路由导航,但路由导航被取消,则需要过滤路由器事件,以便在适当的点触发它-例如,在检查后或导航完成后埃特

    this.myService.methodA()
        .takeUntil(this.router.events.filter(e => e instanceOf NavigationEnd))
        .subscribe(dataA => {
            ...
        });
    
    创建基类

    import { Subject } from 'rxjs/Rx';
    import { OnDestroy } from '@angular/core';
    
     export abstract class Base implements OnDestroy {
    
     protected componentDestroyed$: Subject<any>;
    
    constructor() {
        this.componentDestroyed$ = new Subject<void>();
    
        const destroyFunc = this.ngOnDestroy;
        this.ngOnDestroy = () => {
            destroyFunc.bind(this)();
            this.componentDestroyed$.next();
            this.componentDestroyed$.complete();
        };
    }
    // placeholder of ngOnDestroy. no need to do super() call of extended class.
    public ngOnDestroy() {
        // no-op
    }
    
    }
    
    当你订阅时

    service.takeUntil(this.componentDestroyed$
        .subscribe(...)
    

    这是一个全局级别的更改,无论何时您想要在整个项目中使用相同的方法进行订阅。在任何需要的更改中,您都可以在基类中进行修改

    可能值得注意的是,不应该启动流,流必须直接从组件内完成-任何关键操作都应该通过服务完成,这是不允许的因为大多数都是开放式的,组件在某一点上会停止监听。但我绝对认为,从原则上讲,取消订阅可能是在这里应用的更好模式,因为这就是T应该是逻辑上发生的。我会考虑的。谢谢!考虑<代码>直到(RX.可观察的。计时器(3000))
    在流中。事实上,使用
    takeUntil
    可以
    完成
    ,而使用
    unsubscribe
    可以取消。从某种程度上说,这不是我想要的——我想避免在组件中创建额外的状态工件(
    销毁$
    )然后从
    ngondestory
    触发它。但是我在看了更多之后才意识到,没有语法糖分可以解决这个问题。这肯定已经是一个比存储所有订阅更好的解决方案了。谢谢!angular团队已经讨论了如何使destroy事件更容易被用户访问xj