1
- abstract class ResourceManager < T , K > {
1
+ abstract class ResourceManager < T , Args > {
2
2
private resources : T [ ] = [ ]
3
3
4
- abstract create ( resourceArgs : K ) : T
4
+ abstract create ( resourceArgs : Args ) : T
5
5
abstract destroy ( resource : T ) : void
6
6
7
- add ( resourceArgs : K ) {
7
+ add ( resourceArgs : Args ) {
8
8
const resource = this . create ( resourceArgs )
9
9
this . resources . push ( resource )
10
10
return resource
@@ -27,7 +27,7 @@ class IntervalsManager extends ResourceManager<
27
27
> {
28
28
create ( args : Parameters < typeof setInterval > ) {
29
29
// TODO: use the edge runtime provided `setInterval` instead
30
- return setInterval ( ...args ) [ Symbol . toPrimitive ] ( )
30
+ return webSetIntervalPolyfill ( ...args )
31
31
}
32
32
33
33
destroy ( interval : number ) {
@@ -41,13 +41,49 @@ class TimeoutsManager extends ResourceManager<
41
41
> {
42
42
create ( args : Parameters < typeof setTimeout > ) {
43
43
// TODO: use the edge runtime provided `setTimeout` instead
44
- return setTimeout ( ...args ) [ Symbol . toPrimitive ] ( )
44
+ return webSetTimeoutPolyfill ( ...args )
45
45
}
46
46
47
47
destroy ( timeout : number ) {
48
48
clearTimeout ( timeout )
49
49
}
50
50
}
51
51
52
+ function webSetIntervalPolyfill < TArgs extends any [ ] > (
53
+ callback : ( ...args : TArgs ) => void ,
54
+ ms ?: number ,
55
+ ...args : TArgs
56
+ ) : number {
57
+ return setInterval ( ( ) => {
58
+ // node's `setInterval` sets `this` to the `Timeout` instance it returned,
59
+ // but web `setInterval` always sets `this` to `window`
60
+ // see: https://developer.mozilla.org/en-US/docs/Web/API/Window/setInterval#the_this_problem
61
+ return callback . apply ( globalThis , args )
62
+ } , ms ) [ Symbol . toPrimitive ] ( )
63
+ }
64
+
65
+ function webSetTimeoutPolyfill < TArgs extends any [ ] > (
66
+ callback : ( ...args : TArgs ) => void ,
67
+ ms ?: number ,
68
+ ...args : TArgs
69
+ ) : number {
70
+ const wrappedCallback = ( ) => {
71
+ try {
72
+ // node's `setTimeout` sets `this` to the `Timeout` instance it returned,
73
+ // but web `setTimeout` always sets `this` to `window`
74
+ // see: https://developer.mozilla.org/en-US/docs/Web/API/Window/setTimeout#the_this_problem
75
+ return callback . apply ( globalThis , args )
76
+ } finally {
77
+ // On certain older node versions (<20.16.0, <22.4.0),
78
+ // a `setTimeout` whose Timeout was converted to a primitive will leak.
79
+ // See: https://github.com/nodejs/node/issues/53335
80
+ // We can work around this by explicitly calling `clearTimeout` after the callback runs.
81
+ clearTimeout ( timeout )
82
+ }
83
+ }
84
+ const timeout = setTimeout ( wrappedCallback , ms )
85
+ return timeout [ Symbol . toPrimitive ] ( )
86
+ }
87
+
52
88
export const intervalsManager = new IntervalsManager ( )
53
89
export const timeoutsManager = new TimeoutsManager ( )
0 commit comments