Last active
September 26, 2021 12:50
-
-
Save rigwild/02c28eb3e73075804cb1002a808dc343 to your computer and use it in GitHub Desktop.
Debounced task queue, will skip function calls if serialized arguments were already seen recently
This file contains hidden or 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
export type Task<F extends (...args: any) => any> = { thisArg: any; fn: F; args: Parameters<F> } | |
/** | |
* Debounced task queue, will skip function calls if serialized arguments were already seen recently | |
* | |
* @author rigwild <[email protected]> (https://github.com/rigwild) | |
* @see https://gist.github.com/rigwild/02c28eb3e73075804cb1002a808dc343 | |
*/ | |
export class DebouncedTasksQueue { | |
private debounceMs: number = 10000 | |
private intervalId: ReturnType<typeof setInterval> | undefined | |
private lastSeenTaskCallArgs = new Map<string, number>() | |
tasksQueue: Task<any>[] = [] | |
constructor() {} | |
poll(): Promise<any> { | |
// Cleanup map | |
for (const [k, v] of this.lastSeenTaskCallArgs.entries()) { | |
if (v < Date.now() - this.debounceMs) this.lastSeenTaskCallArgs.delete(k) | |
} | |
const polled = this.tasksQueue.shift() | |
if (polled) { | |
// console.log('polled', polled, this.lastSeenTaskCallArgs) | |
const { thisArg, fn, args } = polled | |
const serializedArgs = args.map(x => JSON.stringify(x)).join() | |
if (!this.lastSeenTaskCallArgs.has(serializedArgs)) { | |
this.lastSeenTaskCallArgs.set(serializedArgs, Date.now()) | |
return Promise.resolve(fn.apply(thisArg, args)).catch(() => {}) | |
} else { | |
// Was debounced, call the next one immediately | |
return this.poll() | |
} | |
} else return Promise.resolve(undefined) | |
} | |
push<F extends (...args: any) => any>(thisArg: any, fn: F, ...args: Parameters<F>): void { | |
this.tasksQueue.push({ thisArg, fn, args }) | |
} | |
clear() { | |
this.tasksQueue = [] | |
} | |
start({ | |
logResult = false, | |
debounceMs = this.debounceMs, | |
nextCallIntervalMs = 500, | |
callImmediately = true | |
}): Promise<any> | undefined { | |
if (debounceMs) this.debounceMs = debounceMs | |
const fn = logResult | |
? async () => { | |
const res = await this.poll() | |
if (res !== undefined) console.log(res) | |
return res | |
} | |
: this.poll | |
this.intervalId = setInterval(fn, nextCallIntervalMs) | |
if (callImmediately) return fn() | |
} | |
stop() { | |
clearInterval(this.intervalId as Parameters<typeof clearInterval>[0]) | |
} | |
} | |
// Usage example | |
const debouncedQueue = new DebouncedTasksQueue() | |
debouncedQueue.start({ logResult: true, debounceMs: 30_000, nextCallIntervalMs: 1000 }) | |
var i = 0 | |
setInterval(() => { | |
i += 100 | |
debouncedQueue.push(this, Math.min, 500, i) | |
if (i > 1500) i = 0 | |
}, 100) | |
// Or just poll manually, will skip debounced tasks automatically and return the proper result | |
console.log(await debouncedQueue.poll()) | |
console.log(await debouncedQueue.poll()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment