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分钟自动触发。有没有人有过这个用例?
答案 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的流。
答案 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())
]
}
并行使用。要检查此行为,请将所有球移至开头,您将看到结果
在您的情况下,您可以执行以下操作:
debounceTime
所以不会经常调用方法,也不会每分钟调用一次(如果没有事件则更少)。
也可以编写自己的实现并将throttleTime
与sudo ln -s $JAVA_HOME/bin/java /usr/bin/java
合并,但我也没有足够的经验提供这样的示例......
答案 2 :(得分:1)
这是我的看法。代码的方式不像Pavel写的那么优雅。
你可以在我准备的plunker中试试。 (您需要打开浏览器控制台才能看到生成的输出流)。您可能还希望使用normalEventDebounceTime
和forcedInterval
关键配置参数,和/或sourceObservable
中的事件时间。
我们的想法是将merge
两个流(sourceObervable
和reschedulingObservable
)合并为一个将由任一输入触发的流。每当合并的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。我赞同伊戈尔 - 这是一个有趣的脑力激荡器,感谢有趣的问题!