关于 rxjs debounceTime 的困惑

Confusion about rxjs debounceTime

我有这个方法,在滚动 table:

时会被多次调用
setViewportRange(firstRow: number, lastRow: number): void {
  this.getViewport(firstRow, lastRow).subscribe(message => {
    console.log(message);
  });
}

我无法控制 setViewportRange 方法的调用,但我需要对其进行去抖动。所以我在 lodash 的 debounce 函数的帮助下创建了这个 debouncedGetViewport 方法:

setViewportRange(firstRow: number, lastRow: number): void {
  this.debouncedGetViewport(firstRow, lastRow);
}

debouncedGetViewport = debounce((firstRow, lastRow) => {
  return this.getViewport(firstRow, lastRow).subscribe(message => {
    console.log(message);
  });
}, 1000);

有效! 但是一位同事问我为什么不使用 RxJs debounce 来代替。所以我尝试用 RxJs 实现它,但我无法让它工作。 debounceTime无论传入哪个值都没有效果!你能帮我理解为什么这不起作用吗?我想我误解了什么。

setViewportRange(firstRow: number, lastRow: number): void {
  this.debouncedGetViewport(firstRow, lastRow);
}

debouncedGetViewport = (firstRow, lastRow) => {
  return this.getViewport(firstRow, lastRow)
    .pipe(debounceTime(1000))
    .subscribe(message => {
      console.log(message);
    });
};

谢谢!

首先不要忘记取消订阅!

首先,当调用 setViewportRange 时,从内存泄漏的角度或多次订阅 this.getViewport 的奇怪行为来确保。你不知道this.getViewport后面发生了什么。可能会发生 getViewport.subscribe 的回调可以被多次调用的情况。始终取消订阅是一种好习惯。

如何退订?有几种方法可以取消订阅 Observable,但在您的情况下,您可以只使用 take 运算符。

debouncedGetViewport = debounce((firstRow, lastRow) => {
  return this.getViewport(firstRow, lastRow).pipe(take(1)).subscribe(message => {
    console.log(message);
  });
}, 1000);

以下是您应该退订的一些资源:

你没有准确描述什么是不工作的!

我根据您的示例问题创建了一个 playground,我想我知道您的意思是:Can you help me to understand why this isn't working.

我想 console.log 被调用了,但是 debounceTime 没有效果,对吧?请确保下次您在问题描述中准确解释什么不起作用。有可能你会被扣分。

为什么你的 debounceTime 不起作用?

我认为 here 是一个很好的 Whosebug 解释,为什么您使用 debounceTime 的示例不起作用!

Consider your logic. You will create a finalized observer for each onChanges. It doesn't debounce because the observer is already finalized and debounce is to prevent emitting one, in the off-chance that another one comes. So it needs at least two emitions to be justifiable (or more ), and that can't happen if the observer is created in the callback.

您似乎正在使用 this.getViewport 创建一个 finalized(已完成)可观察对象,它在发出第一个值后立即完成,这就是 debounceTime 在这里无效的原因。

提示: 如果 observable 到达已经完成,take(1) 没有任何效果,但最好始终取消订阅订阅。

您需要一个不同的解决方案!

unsubscribe$ = new Subject();
rows$: Subject<{firstRow: number, lastRow: number}> = new Subject();

ngOnInit() {
  this.rows$.pipe(
    debounceTime(500),
    switchMap(({firstRow, lastRow}) => this.getViewport(firstRow, lastRow)),
    takeUntil(unsubscribe$)
  ).subscribe(resultOfGetViewport => {
     console.log(resultOfGetViewport);
  });
}


setViewportRange(firstRow: number, lastRow: number) {
  this.rows$.next({firstRow, lastRow});
}

ngOnDestroy() {
  this.unsubscribe$.next();
  this.unsubscribe$.complete();
}

我为前面的代码创建了一个 Stackblitz 示例!

我们不同的解决方案发生了什么?

在我们的新解决方案中,我们不使用最终的可观察对象,因为我们使用 Subject (rows$) 并且 Subject 无法像 getViewport 中那样完成自身。我们必须明确地自己做。我们可以在 takeUntil 运算符中看到这一点。只有当组件被销毁时,所以当 ngOnDestroy 被调用时,我们告诉我们的 rows$ observable 完成它自己。最后但同样重要的是,我们从 getViewportswitchMap 中获取我们的价值。就是这样。

您可能想知道 debounceTimeswitchMap 的顺序在这里是否有所不同。这取决于!如果 this.getViewport 是一个昂贵的操作,那么就把它放在 debounceTime 之后,如果它非常便宜那么顺序无关紧要。

请告诉我我的假设是否正确以及它是否适用于新的解决方案。谢谢。