Last active
February 24, 2023 14:20
-
-
Save jsdw/02ab0da412a96fa53a43a7b095158a58 to your computer and use it in GitHub Desktop.
A function to aggregate values, waiting at least waitMs times before sending off
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function aggregateAndDebounce<A, B>( | |
// Take some val and the current aggregated vals in and return next aggregated vals | |
aggregate: (val: A, vals: B) => B, | |
// This is called waitMs after the some values are given | |
debounced: (val: B) => void, | |
// This produces an initial state for the aggregated vals, to start from/ | |
initialVals: () => B, | |
// How long to wait until calling the debounce | |
waitMs: number | |
): (val: A) => void { | |
let intervalId: number | undefined = undefined | |
let vals = initialVals() | |
let newValsSeen = false | |
function callAggregate(val: A) { | |
vals = aggregate(val, vals) | |
newValsSeen = true | |
} | |
function callDebounced() { | |
if (newValsSeen) { | |
// New vals seen since last fire; send them out. | |
debounced(vals) | |
vals = initialVals() | |
newValsSeen = false | |
} else { | |
// No new vals seen in waitMs since last fire; stop interval | |
clearInterval(intervalId) | |
intervalId = undefined | |
} | |
} | |
return (val: A) => { | |
callAggregate(val) | |
if (!intervalId) { | |
callDebounced() | |
intervalId = setInterval(() => { | |
callDebounced() | |
}, waitMs) | |
} | |
} | |
} | |
// An example usage of the above with concrete types etc. | |
function debounceChannelUpdates( | |
func: (updates: Map<number, number>) => void, | |
waitMs: number | |
): (i: number, l: number) => void { | |
let fn = aggregateAndDebounce( | |
([a, b]: [number,number], map: Map<number, number>) => { | |
map.set(a, b); | |
return map; | |
}, | |
func, | |
() => new Map(), | |
waitMs | |
); | |
return (a: number, b: number) => { | |
fn([a, b]) | |
} | |
} |
Good point! I changed the timeout to an interval and added a thing so that if no new vals are seen since the last time it fired, the interval will finally stop.
setTimeout
is fine; the window.
bit is just assumed anyway (and anyway, in a web worker or whatever window.
isn't available, so omitting it makes the thing able to be used in a web worker context too :)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks James.
There is still an issue—if the timeout has just fired, then a call
< waitMs
later will fire immediately astimeoutId
isundefined
, whereas ideally we want it to debounce forwaitMs - timeSinceLastTriggerMs
.Also,
setTimeout
should bewindow.setTimeout
... I think!