Skip to content

Commit

Permalink
fix(debounceTime): improves performance on quick succession of emits (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
voliva authored Feb 24, 2021
1 parent 1583702 commit 9b70861
Showing 1 changed file with 60 additions and 5 deletions.
65 changes: 60 additions & 5 deletions src/internal/operators/debounceTime.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { asyncScheduler } from '../scheduler/async';
import { MonoTypeOperatorFunction, SchedulerLike } from '../types';
import { debounce } from './debounce';
import { timer } from '../observable/timer';
import { Subscription } from '../Subscription';
import { MonoTypeOperatorFunction, SchedulerAction, SchedulerLike } from '../types';
import { operate } from '../util/lift';
import { OperatorSubscriber } from './OperatorSubscriber';

/**
* Emits a notification from the source Observable only after a particular time span
Expand Down Expand Up @@ -61,6 +62,60 @@ import { timer } from '../observable/timer';
* too frequently.
*/
export function debounceTime<T>(dueTime: number, scheduler: SchedulerLike = asyncScheduler): MonoTypeOperatorFunction<T> {
const duration = timer(dueTime, scheduler);
return debounce(() => duration);
return operate((source, subscriber) => {
let activeTask: Subscription | null = null;
let lastValue: T | null = null;
let lastTime: number | null = null;

const emit = () => {
if (activeTask) {
// We have a value! Free up memory first, then emit the value.
activeTask.unsubscribe();
activeTask = null;
const value = lastValue!;
lastValue = null;
subscriber.next(value);
}
};
function emitWhenIdle(this: SchedulerAction<unknown>) {
// This is called `dueTime` after the first value
// but we might have received new values during this window!

const targetTime = lastTime! + dueTime;
const now = scheduler.now();
if (now < targetTime) {
// On that case, re-schedule to the new target
activeTask = this.schedule(undefined, targetTime - now);
return;
}

emit();
}

source.subscribe(
new OperatorSubscriber(
subscriber,
(value: T) => {
lastValue = value;
lastTime = scheduler.now();

// Only set up a task if it's not already up
if (!activeTask) {
activeTask = scheduler.schedule(emitWhenIdle, dueTime);
}
},
undefined,
() => {
// Source completed.
// Emit any pending debounced values then complete
emit();
subscriber.complete();
},
() => {
// Teardown.
lastValue = activeTask = null;
}
)
);
});
}

0 comments on commit 9b70861

Please sign in to comment.