The RxJS debounce and debounceTime operators are causing a delay of emissions and a rate limit at the same time. That means values are emitted later than occurring in the source and some values are dropped. Generally it only emits when a certain time has passed after the last source emission.
RxJS debounce delays and rate limits
Debounce is not as often used as debounceTime. But it can be important if the debounce rate is variable. The duration for silencing is restarted every time a new value form the source arrives. That means if the rate of source values is higher than the debounce rate no values will be emitted. Let’s see the following example to understand it:
interval(100).pipe(
map(v => v + 1),
take(10),
debounce(sourceValue => timer(500 / sourceValue))
).subscribe({
next: console.log,
});
// 5
// 6
// 7
// 8
// 9
// 10
In above example the debounce duration depends on the source value. The higher the source value, the lower the debounce time. Basically the numbers from 1 to 10 are emitted with a number arriving every 100ms. Because the debounce time for value 5 is 500 / 5 = 100ms, we emit 5. Because during that 100ms no new value has arrived. For value 6 the debounce time is already lower than 100ms. Hence for 6-10 we surely emit the source values because they don’t arrive quicker than the debounce time.
Debounce is important for source values that can occur hundreds of times within a second where only a recent value matters. Hence, it is often used with DOM events like scrolling, mouse movements and inputs from the keyboard.
Use RxJS debounceTime for a fixed silencing window
RxJS debounceTime emits notifications only after a specified timespan has passed without another source emission. Let’s simulate an user input via keyboard with the following source Observable:
const userInput$ = zip(
from(['m', 'my', 'my s', 'my se', 'my search']),
interval(100),
(val, _) => val
);
// m
// my
// my s
// my se
// my search
Every 100ms we get a new search value. If we just consider every value of this source and execute some heavy operations based on it, e.g. HTTP calls, we have unnecessary load. The RxJS debounceTime allows us to only consider values after a certain time has passed without new value:
const userInput$ = zip(
from(['m', 'my', 'my s', 'my se', 'my search']),
interval(100),
(val, _) => val
);
userInput$.pipe(
debounceTime(200),
).subscribe({
next: console.log,
})
// my search
This looks far better! Just “my search” is emitted.
Exercise for the RxJS debounce operator 💪
Determine the scroll position based on the DOM scroll event. The position should only be taken into account when the user hasn’t scrolled during the last 200ms.
You can take this as starting point: index_starter.html.
As always the code examples can be found on GitHub.