mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 10:24:02 +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)
|
if (signal)
|
||||||
racers.push(
|
racers.push(
|
||||||
new Promise((resolveAbort) => {
|
new Promise((resolveAbort) => {
|
||||||
const cancelled = () =>
|
const superseded = {
|
||||||
resolveAbort({
|
ok: false,
|
||||||
ok: false,
|
code: 'CANCELLED',
|
||||||
code: 'CANCELLED',
|
cancelled: true,
|
||||||
cancelled: true,
|
error: 'Superseded.',
|
||||||
error: 'Superseded.',
|
};
|
||||||
});
|
if (signal.aborted) return resolveAbort(superseded);
|
||||||
if (signal.aborted) cancelled();
|
cancelled = () => resolveAbort(superseded);
|
||||||
signal.addEventListener?.('abort', cancelled, { once: true });
|
signal.addEventListener?.('abort', cancelled, { once: true });
|
||||||
|
return undefined;
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
try {
|
try {
|
||||||
return finish(await Promise.race(racers));
|
return finish(await Promise.race(racers));
|
||||||
} finally {
|
} finally {
|
||||||
clearTimeout(timer);
|
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);
|
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 () => {
|
test('resolver disposal aborts provider lifetime and prevents late store/cache writes', async () => {
|
||||||
let release;
|
let release;
|
||||||
let providerSignal;
|
let providerSignal;
|
||||||
|
|||||||
Reference in new issue
Block a user