Skip to content

Commit 8514444

Browse files
committed
test_runner: cancel abort listener when promisified timer settles
1 parent 89fdf6d commit 8514444

File tree

2 files changed

+40
-31
lines changed

2 files changed

+40
-31
lines changed

lib/internal/test_runner/mock/mock_timers.js

Lines changed: 24 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const {
1313
ObjectDefineProperty,
1414
ObjectGetOwnPropertyDescriptor,
1515
ObjectGetOwnPropertyDescriptors,
16-
Promise,
16+
PromiseWithResolvers,
1717
Symbol,
1818
SymbolDispose,
1919
globalThis,
@@ -463,38 +463,31 @@ class MockTimers {
463463
);
464464
}
465465

466-
#promisifyTimer({ timerFn, clearFn, ms, result, options }) {
467-
return new Promise((resolve, reject) => {
468-
if (options?.signal) {
469-
try {
470-
validateAbortSignal(options.signal, 'options.signal');
471-
} catch (err) {
472-
return reject(err);
473-
}
474-
475-
if (options.signal.aborted) {
476-
return reject(abortIt(options.signal));
477-
}
478-
}
466+
async #promisifyTimer({ timerFn, clearFn, ms, result, options }) {
467+
const { promise, resolve, reject } = PromiseWithResolvers();
468+
469+
let abortListener;
470+
if (options?.signal) {
471+
validateAbortSignal(options.signal, 'options.signal');
479472

480-
const onabort = () => {
481-
clearFn(timer);
482-
return reject(abortIt(options.signal));
483-
};
484-
485-
const timer = timerFn(() => {
486-
return resolve(result);
487-
}, ms);
488-
489-
if (options?.signal) {
490-
kResistStopPropagation ??= require('internal/event_target').kResistStopPropagation;
491-
options.signal.addEventListener('abort', onabort, {
492-
__proto__: null,
493-
once: true,
494-
[kResistStopPropagation]: true,
495-
});
473+
if (options.signal.aborted) {
474+
throw abortIt(options.signal);
496475
}
497-
});
476+
477+
abortListener = addAbortListener(options.signal, () => {
478+
reject(abortIt(options.signal));
479+
});
480+
}
481+
482+
const timer = timerFn(resolve, ms);
483+
484+
try {
485+
await promise;
486+
return result;
487+
} finally {
488+
abortListener?.[SymbolDispose]();
489+
clearFn(timer);
490+
}
498491
}
499492

500493
#setImmediatePromisified(result, options) {

test/parallel/test-runner-mock-timers.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,22 @@ describe('Mock Timers Test Suite', () => {
518518
});
519519
});
520520

521+
it('should clear the abort listener when the timer resolves', async (t) => {
522+
t.mock.timers.enable({ apis: ['setTimeout'] });
523+
const expectedResult = 'result';
524+
const controller = new AbortController();
525+
const p = nodeTimersPromises.setTimeout(500, expectedResult, {
526+
ref: true,
527+
signal: controller.signal,
528+
});
529+
530+
assert(hasAbortListener(controller.signal));
531+
532+
t.mock.timers.tick(500);
533+
await p;
534+
assert(!hasAbortListener(controller.signal));
535+
});
536+
521537
it('should reject given an an invalid signal instance', async (t) => {
522538
t.mock.timers.enable({ apis: ['setTimeout'] });
523539
const expectedResult = 'result';

0 commit comments

Comments
 (0)