RxJs订阅呼叫的限制数量

时间:2019-05-09 09:28:35

标签: angular rxjs observable

我收听ResizeObserver(或更确切地说,是一个polyfill)来触发对fabricjs画布的重绘,这是非常耗费资源的。因此,我想限制resize事件触发的重绘次数。

我尝试用RxJs实现此功能:

  1. 重新绘制立即(第一次)触发
  2. 重绘不会在n毫秒内触发
  3. 重绘确实会在n毫秒后触发
  4. 重绘确实会为上一次调整大小事件触发

RxJs提供了一些built-in time-based operators。但是,它们都有缺点:

  • auditTime:有初始延迟
  • debounceTime:初始延迟,当您继续调整大小时永远不会触发
  • throttleTime:可能会忽略最后几个至关重要的事件

我尝试合并/加入这些运算符,但这导致n秒后出现双重呼叫和其他问题。有没有简单的RxJs方法可以做到这一点?我想可以通过启动/清除超时功能来实现。

3 个答案:

答案 0 :(得分:1)

首先想到的是编写自己的管道运算符: https://github.com/ReactiveX/rxjs/blob/master/doc/pipeable-operators.md#build-your-own-operators-easily

或者,您可以尝试如下操作:

3.6+

答案 1 :(得分:1)

您可以将throttleTime与选项trailing一起使用以发出最后一个事件。最后一个事件将以给定的延迟发出,并且当您停止调整大小时不会立即发出。

由于您还希望发出第一个事件,因此您还需要默认选项leading。将两者都设置为true将导致两个事件在每个新时间间隔的结尾和开头直接一个接一个地触发。为防止这种情况,您可以添加debounceTime,且间隔要短一些,例如50ms。

import { fromEvent, asyncScheduler } from 'rxjs';
import { throttleTime, debounceTime, map } from 'rxjs/operators';

const source = fromEvent(window, 'resize').pipe(map(e => e.target['innerWidth']));
const width = source.pipe(
  throttleTime(1000, asyncScheduler, { leading: true, trailing: true }),
  debounceTime(50)
)

https://stackblitz.com/edit/typescript-qsjhvu

答案 2 :(得分:1)

您可以编写自己的函数,并在局部变量中跟踪最后发出和接收的项目。要在Rx中读取时间,您应该使用scheduler.now(),这样可以使您的代码可测试。使用switchMap,您可以使用switchMap(()=>of(e).pipe(delay(duration)))来模拟油门行为。使用此功能,我们可以对最后一个项目很久以前要立即发出下一个项目的情况进行特殊处理。

这导致以下解决方案:

function limitTime(duration, scheduler = async) {
  return (src) => new Observable(ob => {
      var last = scheduler.now() - duration;
      return src.pipe(
          switchMap(e => {
            var last2 = last;
            last = scheduler.now();
            if(last - last2 > duration) {
              return of(e);
            }
            return of(e).pipe(delay(duration + last2 - last, scheduler),
                             tap(() => last = scheduler.now()));
          })
        ).subscribe(ob);
    })
}

通过一些测试here看到它的实际效果。