typescript 关于rxjs去抖时间的混淆

2ledvvac  于 2023-01-31  发布在  TypeScript
关注(0)|答案(1)|浏览(148)

我有一个this方法,在滚动表时会被多次调用:

setViewportRange(firstRow: number, lastRow: number): void {
  this.getViewport(firstRow, lastRow).subscribe(message => {
    console.log(message);
  });
}

我没有setViewportRange方法调用的控制权,但是我需要去抖动,所以我用help lodash的去抖动函数创建了这个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去反跳。所以我试着用RxJs去反跳,但是我不能让它起作用。无论传递哪个值,去反跳时间都没有作用!你能帮我理解为什么它不起作用吗?我想我误解了什么。

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

谢谢大家!

i7uq4tfw

i7uq4tfw1#

首先不要忘记取消订阅!

首先,从内存泄漏的Angular 或在调用setViewportRange时多次订阅this.getViewport时的奇怪行为进行确认。您不知道this.getViewport后面发生了什么。getViewport.subscribe的回调可能会被多次调用。始终取消订阅是一个好习惯。
如何取消订阅?有几种方法可以取消订阅一个观测对象,但在您的情况下,您可以只使用take操作符。

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

以下是您应该取消订阅的一些资源:

您没有准确描述什么不起作用!

我根据您的示例问题创建了一个playground,我想我知道您的意思:“你能帮我理解为什么这是行不通的”。
我猜console.log被调用了,但是debounceTime没有任何效果,对吗?请确保下次您在问题描述中准确解释了什么不起作用。可能会发生这样的情况,您将得到一个负分。

为什么你的debounceTime不工作?

我认为Nuno Sousa给出了一个很好的堆栈溢出解释,说明了为什么您的debounceTime示例不起作用!
考虑一下你的逻辑。你将为每个onChanges创建一个终结的观察者。它不会去抖动,因为观察者已经终结了,去抖动是为了防止发出一个观察者,以防另一个观察者出现。所以它需要至少两个(或更多)发射才是合理的,如果观察者是在回调中创建的,那就不可能发生。
看起来你正在用this.getViewport创建一个finalized(完成的)观察对象,它在发出第一个值后立即完成,这就是debounceTime在这里没有效果的原因。

***提示:***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被调用的时候,我们告诉我们的行$ observable完成它自己,最后但同样重要的是,我们用switchMapgetViewport得到我们的值,就是这样。
你可能想知道debounceTimeswitchMap的顺序在这里是否有区别。这取决于!如果this.getViewport是一个昂贵的操作,那么就把它放在debounceTime之后,如果它非常便宜,那么顺序就不重要了。

相关问题