mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 02:14:01 +00:00
fix(area): detach a lookup's abort listener once it settles
Each resolve() that raced the caller's signal added an abort listener and never removed it, so a long-lived signal collected one listener per completed lookup. The listener is now removed when the call settles and is not added at all to a signal that is already aborted.
This commit is contained in:
1 parent
3d2896cf27
commit
16b383ab6f
2 files changed
+43
-8
No files matched your search
@@ -626,24 +626,29 @@ export function createAreaResolver({
|
||||
);
|
||||
}),
|
||||
);
|
||||
// Detached once this call settles: a caller's long-lived signal must not
|
||||
// collect one listener per completed lookup.
|
||||
let cancelled = null;
|
||||
if (signal)
|
||||
racers.push(
|
||||
new Promise((resolveAbort) => {
|
||||
const cancelled = () =>
|
||||
resolveAbort({
|
||||
ok: false,
|
||||
code: 'CANCELLED',
|
||||
cancelled: true,
|
||||
error: 'Superseded.',
|
||||
});
|
||||
if (signal.aborted) cancelled();
|
||||
const superseded = {
|
||||
ok: false,
|
||||
code: 'CANCELLED',
|
||||
cancelled: true,
|
||||
error: 'Superseded.',
|
||||
};
|
||||
if (signal.aborted) return resolveAbort(superseded);
|
||||
cancelled = () => resolveAbort(superseded);
|
||||
signal.addEventListener?.('abort', cancelled, { once: true });
|
||||
return undefined;
|
||||
}),
|
||||
);
|
||||
try {
|
||||
return finish(await Promise.race(racers));
|
||||
} finally {
|
||||
clearTimeout(timer);
|
||||
if (cancelled) signal.removeEventListener?.('abort', cancelled);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -556,6 +556,36 @@ test('distinct area lookups are hard-bounded while same-key callers still coales
|
||||
assert.equal(b.ok, true);
|
||||
});
|
||||
|
||||
test('settled lookups leave no listener on the caller\'s signal', async () => {
|
||||
const { resolver } = makeResolver();
|
||||
const controller = new AbortController();
|
||||
let added = 0;
|
||||
let removed = 0;
|
||||
const add = controller.signal.addEventListener.bind(controller.signal);
|
||||
const remove = controller.signal.removeEventListener.bind(controller.signal);
|
||||
controller.signal.addEventListener = (...args) => {
|
||||
if (args[0] === 'abort') added += 1;
|
||||
return add(...args);
|
||||
};
|
||||
controller.signal.removeEventListener = (...args) => {
|
||||
if (args[0] === 'abort') removed += 1;
|
||||
return remove(...args);
|
||||
};
|
||||
for (const query of ['Texas', 'Bagmati', 'Nowhere at all'])
|
||||
await resolver.resolve({ query }, { signal: controller.signal });
|
||||
assert.ok(added > 0, 'the lookups listened while running');
|
||||
assert.equal(removed, added, 'every listener is detached once settled');
|
||||
|
||||
const aborted = AbortSignal.abort();
|
||||
let attached = 0;
|
||||
aborted.addEventListener = () => {
|
||||
attached += 1;
|
||||
};
|
||||
const late = await resolver.resolve({ query: 'Punjab, Pakistan' }, { signal: aborted });
|
||||
assert.equal(late.code, 'CANCELLED');
|
||||
assert.equal(attached, 0, 'an aborted signal gets no listener');
|
||||
});
|
||||
|
||||
test('resolver disposal aborts provider lifetime and prevents late store/cache writes', async () => {
|
||||
let release;
|
||||
let providerSignal;
|
||||
|
||||
Reference in new issue
Block a user