简体   繁体   中英

Angular: How to debounce a function call with Observable.timer() and switchMap?

This is my method:

  filter(value) {    
    this.backendCall(value)    
  }

I would like to put a delay between backend calls so that my method does not get called on every keystroke with Observable.timer() and switchMap.

As an example I have this code from an async validator that does precisely what I want :

export function createAsyncValidator(checkFn: (value: string) => Observable<Boolean>, errorMessage: string) {
    return (control: AbstractControl): Observable<ValidationErrors> => {
        return Observable.timer(500).switchMap(() => {
            return checkFn(control.value)
                .map(result => (result ? null : { [errorMessage]: true }));
        });
    }
}

... but I am struggling to apply it to my method . This is what I have tried:

  filter(value) {
    Observable.timer(500).switchMap(() => {
      return Observable.of(value);
    }).subscribe(() => {
      console.log('filter', value);
      // this.backendCall(value)
    });
  }

The delay is indeed applied, but all values are logged . I was expecting the swithMap to unsubsribe those observables that arrive during the delay. What am I missing here ?

Use the debounceTime before subscribing to an observable.

In your filter function emit an event with the value and react in the subscription with added debounceTime.

filter(value: ValueType) {
    this.filterSubject.next(value);
}

ngOnInit() {
    this.filterSubject = new Subject<ValueType>();
    this.filterSubject.debounceTime(500).subscribe((value: ValueType) => {
        this.backendCall(value);
    });
}

First I think you just miss the return statement there :

filter(value) {
    -->return<-- Observable.timer(500).switchMap(() => {
      return Observable.of(value);
    }).subscribe(() => {
      console.log('filter', value);
      // this.backendCall(value)
    });
  }

But i dont really understand the logic you are trying to put here.

EDIT after this comment :

I would like the backend call to happen only when I am done typing (with a delay of a second for example). Throttle would send my first keystroke to the backend right away

You could simply use the debounceTime operator to implements such behavior.

doc here

So if you source is an input you could use it that way :

<myFormControl>.valueChanges.debounceTime(500).flatMap(value => {
    return this.backendCall(value);
}).subscribe();

This way, the backendCall will be called 500ms after you stop typing.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM