RxJS:仅在不同的情况下对流进行去抖动

时间:2017-06-14 22:36:31

标签: javascript angular rxjs redux-observable

我想去除一个流 - 但前提是源值与以前相同。我如何使用RxJS 5进行此操作?

如果值相同并且我之前在指定的时间窗口内发出了值,我不想发出值。我应该能够使用流中的值 - 或者比较distinctUntilChanged的比较函数。

7 个答案:

答案 0 :(得分:2)

我不知道如何在不创建自己的运算符的情况下执行此操作,因为您需要维护某种状态(最后看到的值)。

一种方式看起来像这样:

// I named this debounceDistinctUntilChanged but that might not be
// the best name. Name it whatever you think makes sense!

function debounceDistinctUntilChanged(delay) {
  const source$ = this;

  return new Observable(observer => {
    // Using an object as the default value
    // so that the first time we check it
    // if its the same its guaranteed to be false
    // because every object has a different identity.
    // Can't use null or undefined because source may
    // emit these!
    let lastSeen = {};

    return source$
      .debounce(value => {
        // If the last value has the same identity we'll
        // actually debounce
        if (value === lastSeen) {
          return Observable.timer(delay);
        } else {
          lastSeen = value;
          // This will complete() right away so we don't actually debounce/buffer
          // it at all
          return Observable.empty();
        }
      })
      .subscribe(observer);
  });
}

现在您看到了一个实现,您可能(或可能不会)发现它与您的期望不同。您的描述实际上遗漏了某些细节,例如它应该只是您在去抖时间范围内保留的最后值,或者它是否为一组 - 基本上distinctUntilChanged与{{1} }。我假设了后者。

无论哪种方式,希望这为您提供了一个起点,并揭示了创建自定义运算符是多么容易。内置的运算符绝对不能为所有内容提供解决方案,因此任何足够高级的应用程序都需要自己创建(或者在不抽象的情况下内联执行必要的内容,这也很好)。

然后,您可以将此运算符放在Observable原型上:

distinct

或者使用Observable.prototype.debounceDistinctUntilChanged = debounceDistinctUntilChanged; // later source$ .debounceDistinctUntilChanged(400) .subscribe(d => console.log(d));

let

如果可以,我建议您真正了解我的代码所做的事情,以便将来您能够轻松制作自己的解决方案。

答案 1 :(得分:2)

这取决于你想要做什么;当我尝试做类似的事情时,我遇到了这个问题,基本上是去抖动但是对于不同的物体值有不同的去抖动。

从jayphelps尝试解决方案后,我无法按照我的意愿行事。经过多次来回,原来有一种简单的方法可以做到:groupby。

const priceUpdates = [
  {bid: 10, id: 25},
  {bid: 20, id: 30},
  {bid: 11, id: 25},
  {bid: 21, id: 30},
  {bid: 25, id: 30}
];//emit each person
const source = Rx.Observable.from(priceUpdates);
//group by age
const example = source
  .groupBy(bid => bid.id)
  .mergeMap(group$ => group$.debounceTime(500))

const subscribe = example.subscribe(val => console.log(val));

输出:

[object Object] {
  bid: 11,
  id: 25
}
[object Object] {
  bid: 25,
  id: 30
}

Jsbin:http://jsbin.com/savahivege/edit?js,console

此代码将根据出价ID进行分组并对其进行去抖动,因此只会为每个代码发送最后一个值。

答案 2 :(得分:2)

使用@samberic建议的方法以ngrx效果为RxJS 6+提供答案,以将来自与RxJS 6相同源ID的动作分组。

this.actions$.pipe(
    ofType(actionFoo, actionBar), // Two different ngrx action with an id property
    groupBy(action => action.id), // Group by the id from the source
    mergeMap(action => action.pipe(
        debounceTime(5000)
    ))
).pipe(
    // Do whatever it is that your effect is supposed to do!
)

答案 3 :(得分:2)

这是我的打字稿中的 RXJS 6+ 版本,它按最初的要求 100% 工作。在每个新的源值上去抖动(重新启动计时器)。仅当新值与先前值不同或去抖时间已过期时才发出值。

// custom rxjs operator to debounce while the source emits the same values
debounceDistinct<T>(delay: number) {
    return (source: Observable<T>): Observable<T> => {
        return new Observable(subscriber => {
            let hasValue = false;
            let lastValue: T | null = null;
            let durationSub: Subscription = null;

            const emit = () => {
                durationSub?.unsubscribe();
                durationSub = null;
                if (hasValue) {
                    // We have a value! Free up memory first, then emit the value.
                    hasValue = false;
                    const value = lastValue!;
                    lastValue = null;
                    subscriber.next(value);
                }
            };

            return source.subscribe(
                (value: T) => {
                    // new value received cancel timer
                    durationSub?.unsubscribe();
                    // emit lastValue if the value has changed
                    if (hasValue && value !== lastValue) {
                        const value = lastValue!;
                        subscriber.next(value);
                    }
                    hasValue = true;
                    lastValue = value;
                    // restart timer
                    durationSub = timer(delay).subscribe(() => {
                        emit();
                    });
                },
                error => {
                },
                () => {
                    emit();
                    subscriber.complete();
                    lastValue = null;
                });
        });
    }
}

答案 4 :(得分:0)

rxjs 6的更新:

 source$
.pipe(
        // debounceTime(300),  optionally un-comment this to add debounce
        distinctUntilChanged(),
    )
.subscribe(v => console.log(v))

答案 5 :(得分:0)

此rxjs6 +运算符将在源“值”已更改或自上一次发出后经过一些“延迟”时间(即使“值”未更改)时发出:

export function throttleUntilChanged(delay: number) {
  return (source: Observable<any>) => {
    return new Observable(observer => {

      let lastSeen = {};
      let lastSeenTime = 0;

      return source
        .pipe(
          flatMap((value: any) => {
            const now = Date.now();
            if (value === lastSeen && (now - lastSeenTime) < delay ) {
              return empty();
            } else {
              lastSeen = value;
              lastSeenTime = now;
              return of(value);
            }
          })
        )
        .subscribe(observer);
    });
  };
}

答案 6 :(得分:0)

另一种可能性,但不确定 rxjs5 是否支持:

source$.pipe(
  pairwise(),
  debounce(([a, b]) => {
    if (a === b) {
      return interval(1000)
    }
    return of();
  }),
  map(([a,b]) => b)
)
.subscribe(console.log);

https://stackblitz.com/edit/typescript-39nq7f?file=index.ts&devtoolsheight=50