Skip to content

Commit 3f0ffa1

Browse files
committed
fix node:timers abort callback reset
1 parent 9cea433 commit 3f0ffa1

File tree

2 files changed

+358
-15
lines changed

2 files changed

+358
-15
lines changed

src/node/internal/internal_timers_promises.ts

+21-14
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ import {
3434

3535
const kScheduler = Symbol.for('kScheduler');
3636

37+
type OnCancelCallback = (() => void) | undefined;
38+
3739
export async function setTimeout<T = void>(
3840
delay: number | undefined,
3941
value?: T,
@@ -62,17 +64,21 @@ export async function setTimeout<T = void>(
6264
}
6365

6466
const { promise, resolve, reject } = Promise.withResolvers<T>();
67+
let onCancel: OnCancelCallback;
6568

6669
const timer = timers.setTimeout(() => {
6770
resolve(value as T);
68-
}, delay ?? 0);
71+
if (onCancel) {
72+
signal?.removeEventListener('abort', onCancel);
73+
}
74+
}, delay ?? 1);
6975

7076
if (signal) {
71-
function onCancel(): void {
77+
onCancel = (): void => {
7278
timers.clearTimeout(timer);
73-
reject(new AbortError(undefined, { cause: signal?.reason }));
74-
}
75-
signal.addEventListener('abort', onCancel);
79+
reject(new AbortError(undefined, { cause: signal.reason }));
80+
};
81+
signal.addEventListener('abort', onCancel, { once: true });
7682
}
7783

7884
return promise;
@@ -101,18 +107,21 @@ export async function setImmediate<T>(
101107
}
102108

103109
const { promise, resolve, reject } = Promise.withResolvers<T>();
110+
let onCancel: OnCancelCallback;
104111

105112
const timer = timers.setImmediate(() => {
106113
resolve(value as T);
114+
if (onCancel) {
115+
signal?.removeEventListener('abort', onCancel);
116+
}
107117
});
108118

109119
if (signal) {
110-
function onCancel(): void {
120+
onCancel = (): void => {
111121
timers.clearImmediate(timer);
112-
signal?.removeEventListener('abort', onCancel);
113-
reject(new AbortError(undefined, { cause: signal?.reason }));
114-
}
115-
signal.addEventListener('abort', onCancel);
122+
reject(new AbortError(undefined, { cause: signal.reason }));
123+
};
124+
signal.addEventListener('abort', onCancel, { once: true });
116125
}
117126

118127
return promise;
@@ -144,7 +153,7 @@ export async function* setInterval<T = void>(
144153
throw new AbortError(undefined, { cause: signal.reason });
145154
}
146155

147-
let onCancel: (() => void) | undefined;
156+
let onCancel: OnCancelCallback;
148157
let interval: timers.Timeout;
149158
try {
150159
let notYielded = 0;
@@ -164,14 +173,12 @@ export async function* setInterval<T = void>(
164173
if (signal) {
165174
onCancel = (): void => {
166175
timers.clearInterval(interval);
167-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
168-
signal.removeEventListener('abort', onCancel!);
169176
callback?.(
170177
Promise.reject(new AbortError(undefined, { cause: signal.reason }))
171178
);
172179
callback = undefined;
173180
};
174-
signal.addEventListener('abort', onCancel);
181+
signal.addEventListener('abort', onCancel, { once: true });
175182
}
176183

177184
while (!signal?.aborted) {

0 commit comments

Comments
 (0)