Reputation: 26975
Given an event stream like (each -
is 10ms
)
--A-B--C-D
With debounceTime(20)
we get
-----------D
With throttleTime(20)
we get
--A----C--
With throttleTime(20, undefined, {leading: true, trailing: true}
we get
--A----CD
How can I instead guarantee that I have that much time between each emit, so for example with 20ms
--A-----C--D
In general the throttleTime
with the trailing: true
gets closest, but it can sometimes cause the trailing
output to be too close to the leading
output.
Sample code can be found on rxviz.com
Upvotes: 2
Views: 498
Reputation: 14089
Concatenate an empty delay to each item, that doesn't emit anything and only completes after a given time.
const { EMTPY, of, concat } = Rx;
const { concatMap, delay } = RxOperators;
event$.pipe(
concatMap(item => concat(of(item), EMPTY.pipe(delay(20))))
);
Map every item to a timer that starts with the given item and completes after a given amount of time. The next item will be emitted when the timer completes. Values emitted by the timer itself are ignored.
const { timer } = Rx;
const { concatMap, ignoreElements, startWith } = RxOperators;
event$.pipe(
concatMap(item => timer(20).pipe(ignoreElements(), startWith(item)))
);
If your event stream emits items faster than the desired delay you could use zip
to emit events when an interval emits.
const { interval, zip } = Rx;
const { map } = RxOperators;
zip(event$, interval(20)).pipe(map(([item, i]) => item));
This method won't guarantee n
seconds between every emitted item in all circumstances, e.g. when there is a gap larger than the desired delay followed by a small gap in the event stream.
E.g zip
works in your example with emits at 20, 30, 50, 60 with min delay 20.
zip
won't work perfectly with emits at 20, 30, 65, 70 with min delay 20.
When the interval
emits faster than events are coming in, those interval items will just pile up inside zip
. If this is the case zip
will immediately zip any new event with an already present interval item from its stack causing events to be emitted without the intended delay.
Upvotes: 4
Reputation: 6008
Not sure if there's a ready-made operator available to achieve this (there might be!), but you can do it by timestamping each value and adding necessary delay in between:
delay
each value by appropriate amountconcat
the resulting sequenceHere's an rxviz illustrating it. Code looks like this:
const minTimeBetween = 800
events.pipe(
timestamp(),
scan((a, x) => ({
...x,
delayBy: a === null
? 0
: Math.max(0, minTimeBetween - (x.timestamp - (a.timestamp + a.delayBy)))
}), null),
concatMap(x => of(x.value).pipe(
delay(x.delayBy)
))
);
Upvotes: 0