订阅主题完成后退订

时间:2020-09-17 14:45:13

标签: javascript typescript rxjs

我有一个主题可以订阅事件监听器Observable(fromEvent)。单击按钮停止按钮后,主题完成,并且应删除事件侦听器。目前,我正在跟踪订阅并取消订阅,这也删除了事件侦听器:

const { fromEvent, Subject } = rxjs
const { map } = rxjs.operators

const subject = new Subject()

subject.subscribe(() => console.log('focused'))

const sub = fromEvent(document.querySelector('input'), 'focus').subscribe(subject)

document.querySelector('button').onclick = () => {
  // This is what I would like to avoid
  sub.unsubscribe;
  subject.complete();
}
<script src="https://unpkg.com/rxjs/bundles/rxjs.umd.min.js"></script>
<input>
<button>Stop</button>

是否可以在sub完成后自动取消订阅subject(并删除事件侦听器)?诸如takeUntilComplete(subject)之类的东西,而无需安装其他库。

编辑:此处的示例过于简化。在我的真实情况下,没有按钮可以使主体完成。

3 个答案:

答案 0 :(得分:3)

您有点想这个,这就是您所需要的:

const stop$ = fromEvent(document.querySelector('button'), 'click');

fromEvent(document.querySelector('input'), 'focus').pipe(takeUntil(stop$)).subscribe(subject)

takeUntil中的完成内容将向下传播到订阅的主题。所有订阅和事件监听器都将以单击按钮结束。

WONT发生的唯一事情是,如果主题完成了除按钮单击之外的其他方式,则输入事件仍将触发,尽管订阅者将不再收听。完成向下游传播,但不向上游传播。

创建新的运算符也很容易(简化您的解决方案):

const takeUntilComplete = (subject) => 
  takeUntil(new Observable(o => 
    subject.subscribe(null, null, () => o.next())))

可以简单地用作:

fromEvent(document.querySelector('input'), 'focus').pipe(takeUntilComplete(subject)).subscribe(subject)

答案 1 :(得分:2)

不是很优雅,但是创建了一个新的Observable,它在完成主题工作时发出:

fromEvent(...).pipe(
  takeUntil(
    new Observable((subscriber) =>
      this._trackedInteractions$?.subscribe({ complete: () => subscriber.next() })
    )
  )
).subscribe(subject)

答案 2 :(得分:1)

考虑扩展Subject类以通知其取消订阅/错误/完成,如下所示:

class NotifyStateSubject<T> extends Subject<T>{
  public stopped = new Subject<void>();

  private notify(): void {
    this.stopped.next();
    this.stopped.unsubscribe();
  }

  public error(err: any):void {
    this.notify();
    super.error(err)
  }

  public complete():void{
    this.notify();
    super.complete()
  }

 public unsubscribe():void {
    this.notify();
    super.unsubscribe();
  }
}

然后按以下方式使用它:

fromEvent(...).pipe(takeUntil(subject.stopped$)).subscribe(subject)