如何使用debounceTime但在一段时间后仍会触发该功能?

时间:2017-10-17 07:51:09

标签: angular signalr rxjs

TLDR; 我想使用debounceTime来执行该函数只有在没有被调用的情况下经过了300毫秒。同时,我也希望能够每1分钟触发一次该功能。如果这个过程需要很长时间。否则,该功能仅在过程结束时触发。

基本上,我们的系统有一个很长的过程,会激活很多SignalR更新到客户端。当我在客户端收到服务器命令时,我将向服务器发送2个额外的HTTP请求以获取一些信息。只要服务器向我发送更新,它就会回到服务器上。

  

我正在使用debounceTime来阻止向客户端发送过多请求   server如果两个命令之间的时间在300ms之内。但有一个   服务器不断向客户端发送更新的用例,   例如1小时。这意味着客户端将在1小时和之后触发getItemCount   300毫秒。

export class LeftNavigationComponent implements OnInit, OnDestroy {
    typeACount: number = 0;
    typeBCount: number = 0;    

    constructor(
        private itemService: service.ItemService,        
        private signalR: service.SignalRService) { }

    ngOnInit(): void {
        this.subscriptions = [            
            this.signalR.itemCreated.debounceTime(300).subscribe(item => this.onUpdatingData())]
    }

    onUpdatingData() {
        Promise.all([
            this.itemService.getItemCount(APP_SETTING.TYPE_A),
            this.itemService.getItemCount(APP_SETTING.TYPE_B)])
            .then(response => this.gettingCountDone(response))
    }

    gettingCountDone(response) {
        this.typeACount = <any>response[0];
        this.typeBCount = <any>response[1];        
    }
}

我仍然希望debounceTime阻止向服务器发送太多请求。但它应该足够智能,在收到第一次更新后的每1分钟自动触发。有没有人有过这个用例?

4 个答案:

答案 0 :(得分:2)

帕维尔的答案很接近,但如果我已经理解了这个问题,你想要这个:

ngOnInit(): void {
    const debounced = this.signalR.itemCreated.debounceTime(300).share();
    this.subscriptions = [         
        debounced.subscribe(() => this.onUpdatingData()),
        debounced.switchMap(() => Observable.interval(60000).takeUntil(this.signalR.itemCreated)).subscribe(() => this.onUpdatingData())
    ]
}

此代码将执行以下操作,当创建的项之间的时间大于300ms onUpdatingData()将被调用。之后每次debounced都会发出一个值,就会创建一个1 minit的throttleTime observable。这意味着,如果自上一次emision以来debounced没有为minut发出,那么将执行onUpdatingData(),并且执行一次。

并且改进将是合并observable,因为它们来自相同类型并执行相同的功能,例如:

ngOnInit(): void {
    const debounced = this.signalR.itemCreated.debounceTime(300).share();
    const merged = debounced.switchMap(() => Observable.interval(60000).takeUntil(this.signalR.itemCreated))
    this.subscriptions = [         
      merged.subscribe(() => this.onUpdatingData())
   ]
}

我发布了一个显示工作解决方案的小提琴。在这个小提琴中,mousedown事件模拟了this.signalR.itemCreated的流。

https://jsfiddle.net/llpujol/e6b6o655/

答案 1 :(得分:1)

您可以使用throttleTime (60000)代替debounceTime或与ngOnInit(): void { this.subscriptions = [ this.signalR.itemCreated.debounceTime(300).subscribe(item => this.onUpdatingData()), this.signalR.itemCreated.throttleTime(60000).subscribe(item => this.onUpdatingData()) ] } 并行使用。要检查此行为,请将所有球移至开头,您将看到结果 enter image description here

在您的情况下,您可以执行以下操作:

debounceTime

所以不会经常调用方法,也不会每分钟调用一次(如果没有事件则更少)。

也可以编写自己的实现并将throttleTimesudo ln -s $JAVA_HOME/bin/java /usr/bin/java 合并,但我也没有足够的经验提供这样的示例......

答案 2 :(得分:1)

这是我的看法。代码的方式不像Pavel写的那么优雅。

你可以在我准备的plunker中试试。 (您需要打开浏览器控制台才能看到生成的输出流)。您可能还希望使用normalEventDebounceTimeforcedInterval关键配置参数,和/或sourceObservable中的事件时间。

我们的想法是将merge两个流(sourceObervablereschedulingObservable)合并为一个将由任一输入触发的流。每当合并的observable发出一个事件时,我们会调用reschedulingSubject.next(),从而将reschedulingObservable延迟1000ms(因为它是debounceTime构建的Subject)。

sourceObservable应该是真正独立的,即由用户输入产生,或者 - 在您的情况下 - 由SignalR根据我的理解产生。

const normalEventDebounceTime = 450;
const forcedInterval = 1000;

const sourceObservable = Rx.Observable.create(observer => {
  setTimeout(() => observer.next('event-0'), 0);
  setTimeout(() => observer.next('event-1'), 1000);
  setTimeout(() => observer.next('event-2'), 1100);
  setTimeout(() => observer.next('event-3'), 1500);
  setTimeout(() => observer.next('event-4'), 2000);
  setTimeout(() => observer.next('event-5'), 5000);
  setTimeout(() => observer.next('event-6'), 8000);
  setTimeout(() => observer.complete(), 9000);
});

const reschedulingSubject = new Rx.Subject();

const reschedulingObservable = reschedulingSubject.asObservable().debounceTime(forcedInterval);

const debouncedSourceObservable = sourceObservable.debounceTime(normalEventDebounceTime);

let keepWatching = true;

sourceObservable.subscribe(
  event => {},
  error => {},
  () => {
    keepWatching = false;
    console.info('Source observable is complete. Stop watching please');
  }
);

Rx.Observable
  .merge(debouncedSourceObservable, reschedulingObservable)
  .do(() => {
    if (keepWatching) {
      setTimeout(() => reschedulingSubject.next('forced-next'), 100);
    }
  })
  .subscribe(event => console.info(event));

该代码生成以下流:

event-0
forced-next
event-3
event-4
forced-next
forced-next
event-5
forced-next
forced-next
event-6
Source observable is complete. Stop watching please
forced-next

此代码的优点是:

  • 几乎就是你在问题中提出的问题。 (因为setTimeout(() => reschedulingSubject.next('forced-next'), 100)而说几乎)。
  • 不需要自定义运算符。

缺点是:

  • “如此简单的问题”的相当复杂的代码。
  • 使用Subject这是最后的手段,IMO。

再次,你问了一个非常好的问题。处理这样的谜题总是很有趣。主演这个问题!

答案 3 :(得分:1)

这是我的看法 - 如果我理解正确的问题,我不确定......但是,代码在下面。

compile 'com.android.support:appcompat-v7:26.+'

这产生以下序列:

// this is just simulation of source of events - in the real world it is this.signalR.itemCreated
// values are such that they would be distinguishable from interval numbers.
// and yes, it is Igor's idea :)
const sourceObservable = Observable.create(observer => {
    setTimeout(() => observer.next(100), 0);
    setTimeout(() => observer.next(101), 1000);
    setTimeout(() => observer.next(102), 1100);
    setTimeout(() => observer.next(103), 1500);
    setTimeout(() => observer.next(104), 1700);
    setTimeout(() => observer.next(105), 2100);
    setTimeout(() => observer.next(106), 4200);
    setTimeout(() => observer.next(107), 5000);
    setTimeout(() => observer.next(108), 8000);
});

// debouncing too fast emits
const itemCreated = sourceObservable.debounceTime(300);

// starting timer after last emitted event
// again, in the real world interval should be 1 minute, this is just for illustrative purposes
const timeout = itemCreated.switchMap(() => Observable.interval(2000));

// then just merging those two
// debounceTime(300) - to suppress possible fast timer->source consequent events
// take(12) is just to limit example length, it is not needed in real application
itemCreated.merge(timeout).debounceTime(300).take(12).subscribe((val) => console.log(`${val}`));

PS。我赞同伊戈尔 - 这是一个有趣的脑力激荡器,感谢有趣的问题!