Previous answer works if no variables are to be processed by the pipe. Plus passing 0 and asap to debounceTime (as so: debounceTime(0, asap)) does mean the next operator will be immediately called. In fact, once you use debounceTime you may experience a half second delay anyway, which is way too long in some cases.
So, after long time messing up with iif and friends, I decided to copy paste debounceTime and modify it to take a conditional function. Any ideas / improvements are most welcome:
This is intended for Angular 9 and may raise warnings/errors with previous versions.
Use it just like you would with debounceTime, just pass a conditional function as first boolean parameter. If it is true, it will debounce, if false it will immediately continue.
Example:
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();
}