2017-10-17 70 views
3

TLDR; 我想使用debounceTime來執行函數只有300毫秒已經過去,沒有被調用。與此同時,我也希望能夠每1分鐘觸發一次該功能。如果過程需要很長時間。否則,該功能只會在過程結束時觸發。如何使用debounceTime但在某段時間後仍然會觸發該功能?


基本上,我們的系統有一個很長的過程,會向客戶端發出大量的SignalR更新。當我在客戶端接收到服務器命令時,我會向服務器發送2個額外的HTTP請求以獲取一些信息。所以只要發送給我的服務器更新,它就會重新啓動服務器。

我使用debounceTime防止發送太多的請求回 服務器如果兩個命令之間的時間是300ms以內。但有一個 用例,其中服務器不斷向客戶端發送更新,例如1小時, 。這意味着客戶端將在1小時和 300毫秒處觸發getItemCount。

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防止發送太多的請求到服務器。但它應該足夠聰明,可以在接收到第一次更新後的每一分鐘內自動觸發。有沒有人有過用例?

+0

您是否可以澄清_會在接收到**第一次更新後的每個例如1分鐘後自動觸發** _什麼是「首次更新」。如果您以大理石圖的形式展示了所需行爲的草圖,它也有助於理解您的問題。順便說一句,這是一個很好的工作謎! –

回答

1

以下是一張就可以了。該代碼比Pavel編寫的代碼更不優雅。

您可以在我準備的plunker中試用。 (您需要打開瀏覽器控制檯才能查看生成的輸出流)。您可能還想玩normalEventDebounceTimeforcedInterval關鍵配置參數,和/或使用sourceObservable中的事件時間。

這個想法是將merge兩個流(sourceObervablereschedulingObservable)合成一個,這個流將被任何一個輸入觸發。每當合併的可觀察事件發出事件時,我們呼叫reschedulingSubject.next()因此推遲reschedulingObservable1000ms(因爲它的構造是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的最後一招。

同樣,你問了一個非常好的問題。總是有趣的處理這樣的難題。主演這個問題!

+0

最初,服務器並沒有發給我足夠的信息,所以我需要向他提供額外的查詢。因此,如果他向我發送了100萬次更新,我會提出2 x 1個密爾的請求。它太糟糕了。然後我嘗試了debounceTime。這是正確的做法,但我的問題出來了。感謝你的時間,即使我還沒有回到你對問題的評論。你的運動員看起來非常好,這是我正在尋找的。 – trungk18

+0

是的,我肯定會接受答案,但我只是稍微測試一下。只是一個問題,如果我想在事件6通過後清除超時,那麼您認爲最好的方法是什麼?瞭解所有的事件會非常隨機地發生,並且難以實現。 – trungk18

+0

@ trungk18是的,按照你的意願測試解決方案。我的筆記本電腦已經關閉,因此我的時區已經晚了。我會把'do'換成stop if語句,它應該在sourceObservable完成時設置 –

1

您可以使用throttleTime(60000)代替debounceTime或與debounceTime並行。要檢查這種行爲所有的球移動到開始,你會看到結果 enter image description here

你的情況,例如,您可以執行以下操作:

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

那麼方法就不會調用太頻繁,也是每分鐘一次(如果沒有事件則更少)。

也可以寫自己的實現,並結合debounceTimethrottleTime但我沒有足夠的經驗也提供了這樣的例子...

+1

一個完整的例子(與代碼)將不勝感激。我試圖解決OP的問題,到目前爲止還不是很成功。 –

+0

謝謝你的例子。但是這會是相同的如果我做debounceTime(300).throttleTime(6000)? – trungk18

+0

否 - 如果是火車,它將首先等待300毫秒,然後將最後一個事件發送給下一輛要通過其中的車輛,然後丟棄所有其他車輛6秒,以便您不能發射事件每6秒更多一次。 –

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()) 
    ] 
} 

此代碼將做到以下幾點,當創建的項目之間的時間超過300毫秒onUpdatingData主要()將叫做。在此之後,每當去抖動發出一個值時,1minit的throttleTime觀察值就被創建。這意味着,如果從最後一次emision開始debounced沒有發射minut,onUpdatingData()將被執行,所以一個。

和改進將合併的觀測,因爲他們是來自同一個類型和執行相同的功能,例如像這樣:

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

這是我對了 - 如果我理解正確的問題,這我不知道的......不過,代碼如下。

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

這將產生以下序列:

100 
// 101 skipped here by sourceObservable.debounceTime(300) 
102 
// 103 skipped here by sourceObservable.debounceTime(300) 
104 
105 
// 0 from interval() skipped here by merge().debounceTime(300) 
106 
107 
0 
108 
0 
1 
2 
3 

PS。我同意伊戈爾 - 這是一個有趣的謎題,感謝有趣的問題!

+0

我在一個非常相同的方向思考,並寫了一個非常相同的代碼。就簡單性而言,這一個比我的答案要好 - 使用通過'Subject'無法手動控制序列創建的技巧。這段代碼在正確性方面比我的回答差,最後一行中的'.debounceTime(300)'可能會延遲_any_事件**,包括「強制」預定的**。這對於OP來說可能是或不是一件大事。好的嘗試,亞歷山大 –

+0

@Igor - 確切地說。我們不知道確切的要求,所以,對我來說,聽起來最終的延遲對於OP來說並不是什麼大問題。雖然它很可能不是真的,因爲......(參見開始語句)。 :) –

+0

@Igor - 其實,首先'debounceTime()'可以安全地跳過。另外,對我來說,關心300毫秒的延遲是沒有意義的,稍後我們會等待1分鐘... –

相關問題