I have a function that does an http request based on a parameter. And I want to add some kind of "debounce" functionality. So if the function gets called multiple times in a set time window, I want to combine the parameters into one request instead of making multiple requests.
I want to achieve this with Observables and in Angular. This does not sound that complicated, however I'm not able to get it running, maybe I'm missing something.
For now let's just skip the combining in a single request as this can be done with an aggregated debounce or a Oberservable.buffer. I have trouble combining the single Observables.
Here's what I've tried so far.
I tried using a Subject, as this seemed to be the proper object for this case (https://stackblitz.com/edit/angular-hcn41v?file=src%2Fapp%2Fapp.component.ts).
constructor(private http: HttpClient) {
this.makeRequest('1').subscribe(x => console.log(x))
this.makeRequest('2').subscribe(console.log)
setTimeout(() => {
this.makeRequest('3').subscribe(console.log)
}, 1000)
}
private makeRequest(id: string) {
this.observable = this.observable.pipe(
merge(Observable.of(id).pipe(delay(1)))
)
return this.aggregateDebounce(this.observable)
}
private getUrl(value) {
console.log('getUrl Call', value);
return 'https://jsonplaceholder.typicode.com/posts/1';
}
private aggregateDebounce(ob$) {
const shared$ = ob$.publishReplay(1).refCount()
return shared$.buffer(shared$.debounceTime(75))
}
I expect to have one 'getUrl Call' log for each function call and one result log. However I only get results if I add more than 1 calls to this.makeRequest() and the result is also weird. All previous values are always returned as well. I think I don't fully understand how Subject works in this case.
Another approach (taken from here RXJS: Aggregated debounce) was to create some sort of aggregate debounce (https://stackblitz.com/edit/angular-mx232d?file=src/app/app.component.ts)
constructor(private http: HttpClient) {
this.makeRequest('1').subscribe(x => console.log(x))
this.makeRequest('2').subscribe(console.log)
setTimeout(() => {
this.makeRequest('3').subscribe(console.log)
}, 1000)
}
private makeRequest(id: string) {
this.observable = this.observable.pipe(
merge(Observable.of(id).pipe(delay(1)))
)
return this.aggregateDebounce(this.observable)
}
private getUrl(value) {
console.log('getUrl Call', value);
return 'https://jsonplaceholder.typicode.com/posts/1';
}
private aggregateDebounce(ob$) {
const shared$ = ob$.publishReplay(1).refCount()
return shared$.buffer(shared$.debounceTime(75))
}
In this scenario I have the problem I'm also getting all previous values as well.
In theory (at least to me) both variants sounded plausible, however it seems like I'm missing something. Any wink in the right direction is highly appreciated.
Edit:
As requested I added the final real-world goal.
Imagine a service that requests information from an API. Within 50-75ms you call the service with a certain id. I want to group those ids together to a single request instead of doing 3. And if 100ms later another call to the service is made, a new request will be done