( parent: AbortController, maxListeners?: number, )
| 66 | * @returns Child AbortController |
| 67 | */ |
| 68 | export function createChildAbortController( |
| 69 | parent: AbortController, |
| 70 | maxListeners?: number, |
| 71 | ): AbortController { |
| 72 | const child = createAbortController(maxListeners) |
| 73 | |
| 74 | // Fast path: parent already aborted, no listener setup needed |
| 75 | if (parent.signal.aborted) { |
| 76 | child.abort(parent.signal.reason) |
| 77 | return child |
| 78 | } |
| 79 | |
| 80 | // WeakRef prevents the parent from keeping an abandoned child alive. |
| 81 | // If all strong references to child are dropped without aborting it, |
| 82 | // the child can still be GC'd — the parent only holds a dead WeakRef. |
| 83 | const weakChild = new WeakRef(child) |
| 84 | const weakParent = new WeakRef(parent) |
| 85 | const handler = propagateAbort.bind(weakParent, weakChild) |
| 86 | |
| 87 | parent.signal.addEventListener('abort', handler, { once: true }) |
| 88 | |
| 89 | // Auto-cleanup: remove parent listener when child is aborted (from any source). |
| 90 | // Both parent and handler are weakly held — if either has been GC'd or the |
| 91 | // parent already aborted ({once: true}), the cleanup is a harmless no-op. |
| 92 | child.signal.addEventListener( |
| 93 | 'abort', |
| 94 | removeAbortHandler.bind(weakParent, new WeakRef(handler)), |
| 95 | { once: true }, |
| 96 | ) |
| 97 | |
| 98 | return child |
| 99 | } |
| 100 |
no test coverage detected