如何使可观察到的反跳时间成为条件

时间:2018-10-29 11:55:08

标签: angular rxjs

我对元素输入事件进行了简单的反跳,如下所示:

Observable
        .fromEvent(this.elInput.nativeElement, 'input')
        .debounceTime(2000)
        .subscribe(event => this.onInput(event));

我想根据事件发生时的值来设置防抖条件,这可能吗?

谢谢

2 个答案:

答案 0 :(得分:3)

是的,那是完全可能的。只需使用 input { beats { port => 5044 } } output { elasticsearch { hosts => "localhost:9200" manage_template => false index => "%{[@metadata][beat]}-%{[@metadata][version]}-%{+YYYY.MM.dd}" } stdout { codec => rubydebug { metadata => true } } } 运算符而不是debounce。传递给选择器函数,该函数在调用时会收到先前的运算符通知。

在您的示例中:

debounceTime

选择器函数需要一个Observable .fromEvent(this.elInput.nativeElement, 'input') .debounce(ev => ev.hasSomeValue ? timer(2000) : EMPTY) .subscribe(event => this.onInput(event)); 并等待其发出,然后转发ObservableLike收到的最后一个通知。与debounce一样,所有其他通知也将被丢弃。您可以使用debounceTime立即转发通知,而不会超时(尽管这将是异步的,请参见下文)

来自learn-rxjs

  

尽管反跳时间没有广泛使用,但是当反跳速率可变时,反跳很重要!

注意:,即使内部Observable立刻发出信号,防抖动也会始终异步安排最后一个值的转发。为避免这种情况,您必须创建第二个可观察对象,并使用EMPTY来完全避免filter

答案 1 :(得分:2)

如果管道不处理任何变量,则上一个答案有效。加上将0和asap传递给debounceTime(因此:debounceTime(0, asap))确实意味着将立即调用下一个运算符。实际上,一旦您使用debounceTime,您可能会遇到半秒的延迟,这在某些情况下会太长。

因此,在长时间与iif和朋友搞混之后,我决定复制粘贴debounceTime并将其修改为具有条件功能。任何想法/改进都是最欢迎的:

这是针对Angular 9的,可能会在以前的版本中引发警告/错误。

就像使用debounceTime一样使用它,只需将条件函数作为第一个布尔参数即可。如果为true,它将反跳,如果为false,它将立即继续。

示例:

observable$.pipe(
    conditionalDebounceTime(() => conditionFlag, 1000),
    tap(observedValue ... => ...)
    ...

conditional-debounce-time.ts

import {MonoTypeOperatorFunction, Observable, Operator, SchedulerLike, Subscriber, Subscription, TeardownLogic} from 'rxjs';
import {async} from 'rxjs/internal/scheduler/async';

class ConditionalDebounceTimeSubscriber<T> extends Subscriber<T> {
  private debouncedSubscription: Subscription|null = null;
  private lastValue: T|null = null;
  private hasValue = false;

  constructor(destination: Subscriber<T>,
              private conditionFunc: () => boolean,
              private dueTime: number,
              private scheduler: SchedulerLike) {
    super(destination);
  }

  protected _next(value: T) {
    this.clearDebounce();
    this.lastValue = value;
    this.hasValue = true;
    if (this.conditionFunc()) {
      this.add(this.debouncedSubscription = this.scheduler.schedule(dispatchNext, this.dueTime, this));
    } else {
      (<Subscriber<T>>this.destination).next(this.lastValue);
    }
  }

  protected _complete() {
    this.debouncedNext();
    (<Subscriber<T>>this.destination)!.complete();
  }

  debouncedNext(): void {
    this.clearDebounce();

    if (this.hasValue) {
      const { lastValue } = this;
      // This must be done *before* passing the value
      // along to the destination because it's possible for
      // the value to synchronously re-enter this operator
      // recursively when scheduled with things like
      // VirtualScheduler/TestScheduler.
      this.lastValue = null;
      this.hasValue = false;
      (<Subscriber<T>>this.destination)!.next(lastValue!);
    }
  }

  private clearDebounce(): void {
    const debouncedSubscription = this.debouncedSubscription;

    if (debouncedSubscription !== null) {
      this.remove(debouncedSubscription);
      debouncedSubscription.unsubscribe();
      this.debouncedSubscription = null;
    }
  }
}

class ConditionalDebounceTime<T> implements Operator<T, T> {
  constructor(private conditionFunc: () => boolean, private dueTime: number, private scheduler: SchedulerLike) {
  }

  call(subscriber: Subscriber<T>, source: any): TeardownLogic {
    return source.subscribe(new ConditionalDebounceTimeSubscriber(subscriber, this.conditionFunc, this.dueTime, this.scheduler));
  }
}

export function conditionalDebounceTime<T>(conditionFunc: () => boolean, dueTime: number, scheduler: SchedulerLike = async): MonoTypeOperatorFunction<T> {
  return (source: Observable<T>) => source.lift(new ConditionalDebounceTime(conditionFunc, dueTime, scheduler));
}

function dispatchNext(subscriber: ConditionalDebounceTimeSubscriber<any>) {
  subscriber.debouncedNext();
}