fix: prevent memory leaks from AbortController closures (#12024)

This commit is contained in:
Max Leiter
2026-02-03 15:51:26 -08:00
committed by GitHub
parent acac05f22e
commit 93e060272a
5 changed files with 184 additions and 15 deletions

View File

@@ -0,0 +1,35 @@
/**
* Creates an AbortController that automatically aborts after a timeout.
*
* Uses bind() instead of arrow functions to avoid capturing the surrounding
* scope in closures. Arrow functions like `() => controller.abort()` capture
* request bodies and other large objects, preventing GC for the timer lifetime.
*
* @param ms Timeout in milliseconds
* @returns Object with controller, signal, and clearTimeout function
*/
export function abortAfter(ms: number) {
const controller = new AbortController()
const id = setTimeout(controller.abort.bind(controller), ms)
return {
controller,
signal: controller.signal,
clearTimeout: () => globalThis.clearTimeout(id),
}
}
/**
* Combines multiple AbortSignals with a timeout.
*
* @param ms Timeout in milliseconds
* @param signals Additional signals to combine
* @returns Combined signal that aborts on timeout or when any input signal aborts
*/
export function abortAfterAny(ms: number, ...signals: AbortSignal[]) {
const timeout = abortAfter(ms)
const signal = AbortSignal.any([timeout.signal, ...signals])
return {
signal,
clearTimeout: timeout.clearTimeout,
}
}