From 725e08330577ee4085e5da7659bf799fd64fe4d4 Mon Sep 17 00:00:00 2001 From: Christophe Eymard Date: Thu, 6 Jun 2019 11:44:55 +0200 Subject: [PATCH] fixed throttle implementation which did not do what was asked --- src/observable.ts | 68 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 48 insertions(+), 20 deletions(-) diff --git a/src/observable.ts b/src/observable.ts index 90548eb..42872d4 100644 --- a/src/observable.ts +++ b/src/observable.ts @@ -191,6 +191,7 @@ export interface ReadonlyObservable { removeObserver(ob: ReadonlyObserver): void debounce(getms: number, setms?: number): ReadonlyObservable + throttle(getms: number, setms?: number): ReadonlyObservable isGreaterThan(rhs: RO): ReadonlyObservable isLesserThan(rhs: RO): ReadonlyObservable equals(rhs: RO): ReadonlyObservable @@ -340,6 +341,37 @@ export class Observable implements ReadonlyObservable { return obs } + /** + * Return a new observable which is a copy of this one that will throttle + * the get / set operations. + * + * Debounced notifies means that observers will only be called every `notifyms` + * milliseconds. + * + * The setms parameter debounces set and assign calls so to prevent setting + * the observable to often with new values. + * + * As with all debounced() operations, only the last value is taken into account. + * + * @param notifyms The number of milliseconds to debounce the notify operation + * @param setms The number of milliseconds to debounce the set operation + */ + throttle(notifyms: number, setms?: number): Observable { + if (setms === undefined) + setms = notifyms + + const obs = this.tf(v => v, (n, o, ob) => ob.set(n)) + + if (setms) { + obs.set = o.throttle(obs.set, setms) + obs.assign = o.throttle(obs.assign, setms) + } + + if (notifyms) obs.notify = o.throttle(obs.notify, notifyms) + + return obs + } + /** * Set the value of the observable and notify the observers listening * to this object of this new value. @@ -1417,13 +1449,6 @@ export class ArrayTransformObservable extends VirtualObservable { export function throttle(ms: number, leading?: boolean): (target: any, key: string, desc: PropertyDescriptor) => void export function throttle(fn: F, ms: number, leading?: boolean): F export function throttle(fn: any, ms: any, leading: boolean = false): any { - var timer: number | null - var prev_res: any - var lead = false - var last_call: number - var _args: any - var self: any - // Called as a method decorator. if (typeof fn === 'number') { leading = ms @@ -1434,32 +1459,35 @@ export class ArrayTransformObservable extends VirtualObservable { } } + var timer: number | null + var prev_res: any + var last_call: number = 0 + var _args: any + var self: any + return function (this: any, ...args: any[]) { - var now = Date.now().valueOf() + var now = Date.now() - // console.log(leading, lead, timer, last_call, now) - if (leading && !lead && !timer && (!last_call || now - last_call >= ms)) { + // If the delay expired or if this is the first time this function is called, + // then trigger the call. Otherwise, we will have to set up the call. + if ((leading && last_call === 0) || last_call + ms <= now) { prev_res = fn.apply(this, args) - lead = true - } else { - lead = false + last_call = now + return prev_res } self = this _args = args + if (!timer) { timer = window.setTimeout(function () { - if (!lead) { - prev_res = fn.apply(self, _args) - last_call = Date.now().valueOf() - } - lead = false + prev_res = fn.apply(self, _args) + last_call = Date.now() _args = null timer = null - }, ms - last_call ? (now - last_call) : 0) + }, ms - (now - (last_call || now))) } - last_call = now return prev_res } }