网站搜索

javascript - 当已订阅的主题完成时取消订阅


我有一个主题,它订阅了一个事件监听器 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>

是否有可能在 subject 完成时自动取消订阅 sub(并移除事件监听器)?类似于 takeUntilComplete(subject),而不安装额外的库。

编辑:这里的例子过于简单化。在我的实际情况中,没有按钮让受试者完成。

答案#1

你有点想太多了,这就是你所需要的全部:

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

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

takeUntil 的完成事件将向订阅的主题传播。所有订阅和事件监听器将在按钮点击时结束。

唯一不会发生的情况是,如果主题以除按钮点击以外的方式完成,那么您的输入事件仍然会触发,尽管订阅者不再监听。完成会向下游传播,但不会向上游传播。

或者,创建新的操作符相当容易(基于你的解决方案进行改编):

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

可以像这样简单使用:

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

答案#2

不是很优雅,但创建一个在 subject 完成时发出值的新 Observable 是可行的:

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