fix throttling

This commit is contained in:
Андрей Бабушкин 2025-05-13 17:53:17 +02:00
parent f8f16210a4
commit f1d8aa7b9a
2 changed files with 52 additions and 38 deletions

View file

@ -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<K, Args extends any[]>(
fn: (key: K, ...args: Args) => void,
interval: number
): (key: K, ...args: Args) => void {
const lastCalls = new Map<K, number>();
const timeouts = new Map<K, ReturnType<typeof setTimeout>>();
const lastArgs = new Map<K, Args>();
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<number, [Element, string]>(
(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()
}
}

View file

@ -320,3 +320,48 @@ export function simpleMerge<T>(defaultObj: T, givenObj: Partial<T>): T {
return result
}
export function throttleWithTrailing<K, Args extends any[]>(
fn: (key: K, ...args: Args) => void,
interval: number
): ((key: K, ...args: Args) => void) & { clear: () => void } {
const lastCalls = new Map<K, number>();
const timeouts = new Map<K, ReturnType<typeof setTimeout>>();
const lastArgs = new Map<K, Args>();
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;
}