debounceTime only after first value

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.


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

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

Tags:

Rxjs

Debounce