From f1d8aa7b9addf5bb0b96d2afa6aa621ac927e6f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=91=D0=B0=D0=B1?= =?UTF-8?q?=D1=83=D1=88=D0=BA=D0=B8=D0=BD?= Date: Tue, 13 May 2025 17:53:17 +0200 Subject: [PATCH] fix throttling --- .../tracker/src/main/app/observer/observer.ts | 45 +++---------------- tracker/tracker/src/main/utils.ts | 45 +++++++++++++++++++ 2 files changed, 52 insertions(+), 38 deletions(-) diff --git a/tracker/tracker/src/main/app/observer/observer.ts b/tracker/tracker/src/main/app/observer/observer.ts index 6621d8fe5..61666a5cd 100644 --- a/tracker/tracker/src/main/app/observer/observer.ts +++ b/tracker/tracker/src/main/app/observer/observer.ts @@ -1,4 +1,4 @@ -import { createMutationObserver } from '../../utils.js' +import { createMutationObserver, throttleWithTrailing } from '../../utils.js' import { RemoveNodeAttribute, SetNodeAttributeURLBased, @@ -29,40 +29,6 @@ import { nextID } from "../../modules/constructedStyleSheets.js"; const iconCache = {} const svgUrlCache = {} -function throttleWithTrailing( - fn: (key: K, ...args: Args) => void, - interval: number -): (key: K, ...args: Args) => void { - const lastCalls = new Map(); - const timeouts = new Map>(); - const lastArgs = new Map(); - - return function (key: K, ...args: Args) { - const now = Date.now(); - const lastCall = lastCalls.get(key) ?? 0; - const remaining = interval - (now - lastCall); - - lastArgs.set(key, args); - - if (remaining <= 0) { - if (timeouts.has(key)) { - clearTimeout(timeouts.get(key)!); - timeouts.delete(key); - } - lastCalls.set(key, now); - fn(key, ...args); - } else if (!timeouts.has(key)) { - const timeoutId = setTimeout(() => { - lastCalls.set(key, Date.now()); - timeouts.delete(key); - const finalArgs = lastArgs.get(key)!; - fn(key, ...finalArgs); - }, remaining); - timeouts.set(key, timeoutId); - } - }; -} - async function parseUseEl( useElement: SVGUseElement, mode: 'inline' | 'dataurl' | 'svgtext', @@ -308,6 +274,7 @@ export default abstract class Observer { this.indexes.length = 1 this.attributesMap.clear() this.textSet.clear() + this.throttledSetNodeData.clear(); } /** @@ -447,7 +414,10 @@ export default abstract class Observer { this.app.attributeSender.sendSetAttribute(id, name, value) } - private throttledSetNodeData = throttleWithTrailing(this.sendNodeData, 30); + private throttledSetNodeData = throttleWithTrailing( + (id, parentElement, data) => this.sendNodeData(id, parentElement, data), + 30 + ); private sendNodeData(id: number, parentElement: Element, data: string): void { if (hasTag(parentElement, 'style')) { @@ -607,7 +577,6 @@ export default abstract class Observer { // for text node id != 0, hence parentID !== undefined and parent is Element this.app.send(CreateTextNode(id, parentID as number, index)) this.throttledSetNodeData(id, parent as Element, node.data) - // this.sendNodeData(id, parent as Element, node.data) } return true } @@ -629,7 +598,6 @@ export default abstract class Observer { } // for text node id != 0, hence parent is Element this.throttledSetNodeData(id, parent as Element, node.data) - // this.sendNodeData(id, parent as Element, node.data) } return true } @@ -678,5 +646,6 @@ export default abstract class Observer { disconnect(): void { this.observer.disconnect() this.clear() + this.throttledSetNodeData.clear() } } diff --git a/tracker/tracker/src/main/utils.ts b/tracker/tracker/src/main/utils.ts index 4b510ef86..83cd41b73 100644 --- a/tracker/tracker/src/main/utils.ts +++ b/tracker/tracker/src/main/utils.ts @@ -320,3 +320,48 @@ export function simpleMerge(defaultObj: T, givenObj: Partial): T { return result } + +export function throttleWithTrailing( + fn: (key: K, ...args: Args) => void, + interval: number +): ((key: K, ...args: Args) => void) & { clear: () => void } { + const lastCalls = new Map(); + const timeouts = new Map>(); + const lastArgs = new Map(); + + const throttled = function (key: K, ...args: Args) { + const now = Date.now(); + const lastCall = lastCalls.get(key) ?? 0; + const remaining = interval - (now - lastCall); + + lastArgs.set(key, args); + + if (remaining <= 0) { + if (timeouts.has(key)) { + clearTimeout(timeouts.get(key)!); + timeouts.delete(key); + } + lastCalls.set(key, now); + fn(key, ...args); + } else if (!timeouts.has(key)) { + const timeoutId = setTimeout(() => { + lastCalls.set(key, Date.now()); + timeouts.delete(key); + const finalArgs = lastArgs.get(key)!; + fn(key, ...finalArgs); + }, remaining); + timeouts.set(key, timeoutId); + } + }; + + throttled.clear = () => { + for (const timeout of timeouts.values()) { + clearTimeout(timeout); + } + timeouts.clear(); + lastArgs.clear(); + lastCalls.clear(); + }; + + return throttled; +} \ No newline at end of file