First of all don`t forget to unsubscribe!
First of all make sure from memory leak perspective or strange behaviour when subscribing multiple times to this.getViewport
when setViewportRange
is called. You did not know what happens behind this.getViewport
. It can happen that the callback of getViewport.subscribe can be called multiple times. It is good practice to always unsubscribe.
How you can unsubscribe? There are several ways for unsubscribing from an Observable but in your case you can just use the take
operator.
debouncedGetViewport = debounce((firstRow, lastRow) => {
return this.getViewport(firstRow, lastRow).pipe(take(1)).subscribe(message => {
console.log(message);
});
}, 1000);
Here are some resources why you should unsubscribe:
You did not exactly describe what is not working!
I created a playground based on your example issue and I think I know what do you mean with: "Can you help me to understand why this isn't working".
I guess the console.log is called but the debounceTime
has no effect, right? Please make sure next time that you explain in your issue description exactly what is not working. It can happen that you will be scored with a minus point.
Why is your debounceTime not working?
I think here is a good Stack Overflow explanation from Nuno Sousa why your example with debounceTime
is not working!
Consider your logic. You will create a finalized observer for each onChanges. It doesn't debounce because the observer is already finalized and debounce is to prevent emitting one, in the off-chance that another one comes. So it needs at least two emitions to be justifiable (or more ), and that can't happen if the observer is created in the callback.
It seems you are creating with this.getViewport
a finalized (completed) observable which completes right after emitting the first value and thats the reason why debounceTime
has here no effect.
Tip: take(1)
has no effect if the observable arrives already finalized but it is a best practice to always unsubscribe the subscription.
You need a different solution!
unsubscribe$ = new Subject();
rows$: Subject<{firstRow: number, lastRow: number}> = new Subject();
ngOnInit() {
this.rows$.pipe(
debounceTime(500),
switchMap(({firstRow, lastRow}) => this.getViewport(firstRow, lastRow)),
takeUntil(unsubscribe$)
).subscribe(resultOfGetViewport => {
console.log(resultOfGetViewport);
});
}
setViewportRange(firstRow: number, lastRow: number) {
this.rows$.next({firstRow, lastRow});
}
ngOnDestroy() {
this.unsubscribe$.next();
this.unsubscribe$.complete();
}
I have created for the previous code a Stackblitz example!
What is happening in our different solution?
In our new solution we do not use a finalized observable because we use a Subject (rows$)
and a Subject can not complete itself as in getViewport
. We must explicitly do it ourselves. We can see this in takeUntil operator. Only when the component is destroyed, so when ngOnDestroy
is called we tell our rows$ observable to complete itself. Last but not least we get our value from getViewport
with switchMap
. Thats it.
You might wonder if the order of debounceTime
and switchMap
makes a difference here. It depends! If this.getViewport
is an expensive operation, then place it right after debounceTime
and if it is very cheap then the order doesn't matter.