Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

debounceTime only after first value

Tags:

rxjs

debounce

Is there a simple way to make debounceTime instant on the first value?

searchQueries.pipe(debounceTime(1000))

let's say i'm debouncing search queries to 1 second.

My understanding is that this will cause a 1 second delay on the first search, But, I want the first search query to be instant.

(e.g. in this example https://stackblitz.com/edit/typescript-adheqt?file=index.ts&devtoolsheight=50 if i type 123 quickly, it will only log 123, but i want it to log 1 and then log 123)

i could try something like

merge(searchQueries.pipe(first()),searchQueries.pipe(debounceTime(1000)))

but then that would potentially add a delay to a second search but is probably good enough.

Is there a way of configuring debounceTime that i'm missing? or should I potentially be using throttle or something else?

like image 420
robert king Avatar asked Mar 12 '19 21:03

robert king


People also ask

How does debounceTime work?

debounceTime delays the values emitted by a source for the given due time. If within this time a new value arrives, the previous pending value is dropped and the timer is reset. In this way debounceTime keeps track of most recent value and emits that most recent value when the given due time is passed.

What is the De bounce time?

The Debounce Time is the interval that must pass before a second pressing of a key is accepted. You can set this interval with the "Debounce time (sec)" slider. (See Figure B-4.) This delay can range from zero to five seconds. Previous: Turning BounceKeys On and Off.

What is debounce time in angular?

debounceTimelinkEmits a notification from the source Observable only after a particular time span has passed without another source emission.


3 Answers

You could use multicast or maybe even throttleTime:

searchQueries.pipe(
  multicast(new Subject(), s => merge(
    s.pipe(take(1)),
    s.pipe(skip(1), debounceTime(1000)),
  )),
);

Since RxJS 6 the throttleTime operator accepts a config parameter where you can tell it to emit both leading and trailing emissions. Maybe this will do what you want instead of debounceTime.

searchQueries.pipe(
  throttleTime(1000, undefined, { leading: true, trailing: true }),
);
like image 197
martin Avatar answered Sep 22 '22 10:09

martin


Here's my 2 cents / an answer I modified from another post with java-ngrx.

dbounce-time-after.ts

import { OperatorFunction, SchedulerLike, concat } from "rxjs";
import { async } from "rxjs/internal/scheduler/async";
import { debounceTime, publish, take } from "rxjs/operators";

export function debounceTimeAfter(
  amount: number,
  dueTime: number,
  scheduler: SchedulerLike = async,
): OperatorFunction<number, number> {
  return publish(value =>
    concat(
      value.pipe(take(amount)),
      value.pipe(debounceTime(dueTime, scheduler))),
    )
  );
}

export function debounceTimeAfterFirst(
  dueTime: number,
  scheduler: SchedulerLike = async,
): OperatorFunction<number, number> {
  return debounceTimeAfter(1, dueTime, scheduler);
}

example.ts

of(1, 2, 3, 4, 5)
  .pipe(
    tap(value => console.log("TAP", value)),
    debounceTimeAfterFirst(50)
  )
  .subscribe(value => console.log(value));

console

TAP 1
1
TAP 2
TAP 3
TAP 4
TAP 5
5

But you could also start debouncing after n number of emits with debounceTimeAfter.

like image 25
christo8989 Avatar answered Sep 19 '22 10:09

christo8989


It's more flexible to use debounce instead of debounceTime.

searchQueries.pipe(debounceTime(1000))

is equal to:

searchQueries.pipe(debounce(() => timer(1000))

You can create your own heuristic to determine what timeout needs to be used. For example:

searchQueries.pipe(debounce(() => timer(getTimeout()))

...

const getTimeout = () => {
    return iterations === 1 ? 0 : 1000;
};

In that scenario you need to track the iterations count on your own and increase it with each value but there are many ways to do it without messing a lot with the code. I simply created a wrapped observable object that contains the original observable and the counting logic. Something like this:

export default class ObservableWrapper {

    ...

    next(parameters) {
        this.iterations++;
        this.observable.next(parameters);
    }
}
like image 27
Wojtek Majerski Avatar answered Sep 18 '22 10:09

Wojtek Majerski