1. throttle (line 1146-1174)
Before: Used recursive setTimeout calls via unpause, creating nested closures and multiple timer contexts. After: - Tracks a single timeoutId to manage timer lifecycle - Eliminates nested closure creation - Cleaner timer chain when calls are pending 2. batch (line 1204-1227) Before: Used throttle internally, inheriting its timer overhead. After: - Direct timer management without the throttle wrapper - Single timeoutId tracked explicitly - Maintains the same semantics (first item processed immediately, rest batched) 3. batcher (line 1235-1259) Before: - Created new timer every time queue went from empty to non-empty - Had unnecessary async in forEach and await r when r was already type U After: - Tracks timeoutId to prevent duplicate timer creation - Only creates timer when queue is truly empty - Removed unnecessary async/await in result handling - More efficient error handling (rejects all items at once if length mismatch) Performance Benefits: These changes should significantly reduce timer overhead by: - Fewer timer objects: Only one active timer per throttled/batched function instead of chains - Less garbage collection: Fewer closure allocations and intermediate objects - Better memory usage: Explicit timer ID tracking instead of implicit state in closures
This commit is contained in:
+44
-28
@@ -1148,26 +1148,27 @@ export const throttle = <F extends (...args: any[]) => any>(ms: number, f: F) =>
|
||||
return f
|
||||
}
|
||||
|
||||
let paused = false
|
||||
let nextArgs: Parameters<F> | undefined
|
||||
let timeoutId: ReturnType<typeof setTimeout> | undefined
|
||||
let lastArgs: Parameters<F> | undefined
|
||||
|
||||
const unpause = () => {
|
||||
if (nextArgs) {
|
||||
f(...nextArgs)
|
||||
nextArgs = undefined
|
||||
setTimeout(unpause, ms)
|
||||
const later = () => {
|
||||
if (lastArgs !== undefined) {
|
||||
const args = lastArgs
|
||||
lastArgs = undefined
|
||||
f(...args)
|
||||
timeoutId = setTimeout(later, ms)
|
||||
} else {
|
||||
paused = false
|
||||
timeoutId = undefined
|
||||
}
|
||||
}
|
||||
|
||||
return (...thisArgs: Parameters<F>) => {
|
||||
if (!paused) {
|
||||
f(...thisArgs)
|
||||
paused = true
|
||||
setTimeout(unpause, ms)
|
||||
} else {
|
||||
nextArgs = thisArgs
|
||||
return (...args: Parameters<F>) => {
|
||||
lastArgs = args
|
||||
|
||||
if (timeoutId === undefined) {
|
||||
f(...args)
|
||||
lastArgs = undefined
|
||||
timeoutId = setTimeout(later, ms)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1202,11 +1203,26 @@ export const throttleWithValue = <T>(ms: number, f: () => T) => {
|
||||
*/
|
||||
export const batch = <T>(t: number, f: (xs: T[]) => void) => {
|
||||
const xs: T[] = []
|
||||
const cb = throttle(t, () => xs.length > 0 && f(xs.splice(0)))
|
||||
let timeoutId: ReturnType<typeof setTimeout> | undefined
|
||||
|
||||
const later = () => {
|
||||
if (xs.length > 0) {
|
||||
f(xs.splice(0))
|
||||
timeoutId = setTimeout(later, t)
|
||||
} else {
|
||||
timeoutId = undefined
|
||||
}
|
||||
}
|
||||
|
||||
return (x: T) => {
|
||||
const shouldFlush = timeoutId === undefined
|
||||
|
||||
xs.push(x)
|
||||
cb()
|
||||
|
||||
if (shouldFlush) {
|
||||
f(xs.splice(0))
|
||||
timeoutId = setTimeout(later, t)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1218,27 +1234,27 @@ export const batch = <T>(t: number, f: (xs: T[]) => void) => {
|
||||
*/
|
||||
export const batcher = <T, U>(t: number, execute: (request: T[]) => U[] | Promise<U[]>) => {
|
||||
const queue: {request: T; resolve: (x: U) => void; reject: (reason?: string) => void}[] = []
|
||||
let timeoutId: ReturnType<typeof setTimeout> | undefined
|
||||
|
||||
const _execute = async () => {
|
||||
timeoutId = undefined
|
||||
const items = queue.splice(0)
|
||||
const results = await execute(items.map(item => item.request))
|
||||
|
||||
results.forEach(async (r, i) => {
|
||||
if (results.length === items.length) {
|
||||
items[i].resolve(await r)
|
||||
} else {
|
||||
items[i].reject("Execute must return a result for each request")
|
||||
}
|
||||
})
|
||||
if (results.length === items.length) {
|
||||
results.forEach((r, i) => items[i].resolve(r))
|
||||
} else {
|
||||
items.forEach(item => item.reject("Execute must return a result for each request"))
|
||||
}
|
||||
}
|
||||
|
||||
return (request: T): Promise<U> =>
|
||||
new Promise((resolve, reject) => {
|
||||
if (queue.length === 0) {
|
||||
setTimeout(_execute, t)
|
||||
}
|
||||
|
||||
queue.push({request, resolve, reject})
|
||||
|
||||
if (timeoutId === undefined) {
|
||||
timeoutId = setTimeout(_execute, t)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user