Guarantee `n` seconds between emit without waiting initially

亡梦爱人 提交于 2019-12-10 11:17:47

问题


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


回答1:


1. Concat a delay

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))))
);

2. ConcatMap to a timer

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)))
);

3. Zip with an interval (not optimal)

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.

https://rxviz.com/v/2ORZLllO




回答2:


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:

  1. Timestamp each value
  2. Scan over the sequence and calculate relative delay based on previous value's effective timestamp
  3. delay each value by appropriate amount
  4. concat the resulting sequence

Here'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)
  ))
);


来源:https://stackoverflow.com/questions/54617220/guarantee-n-seconds-between-emit-without-waiting-initially

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!