Skip to content

Commit 840c375

Browse files
committed
fix(core): do not save point-in-time setTimeout and rAF references (#55124)
Grabbing and saving the references to global `setTimeout` and `rAF` implementations at a certain point in time can be problematic, espcially in tests. Tests might call something like `jasmine.clock().install();` and `jasmine.clock().uninstall();`. If the install happens before we grab the implementation and then the uninstall happens after, our scheduling function will be broken because it would have saved a reference to the jasmine `setTimeout` implementation, which would have since been cleaned up and will throw an error when attempting to access `delayedFunctionScheduler`. There are other scenarios that may apply, not even just for tests, when patches are applied and removed to the globals. PR Close #55124
1 parent e18a0ed commit 840c375

File tree

16 files changed

+87
-64
lines changed

16 files changed

+87
-64
lines changed

packages/core/src/change_detection/scheduling/zoneless_scheduling_impl.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {inject} from '../../di/injector_compatibility';
1212
import {EnvironmentProviders} from '../../di/interface/provider';
1313
import {makeEnvironmentProviders} from '../../di/provider_collection';
1414
import {PendingTasks} from '../../pending_tasks';
15-
import {getCallbackScheduler} from '../../util/callback_scheduler';
15+
import {scheduleCallback} from '../../util/callback_scheduler';
1616
import {NgZone, NoopNgZone} from '../../zone/ng_zone';
1717

1818
import {ChangeDetectionScheduler, NotificationType, ZONELESS_ENABLED, ZONELESS_SCHEDULER_DISABLED} from './zoneless_scheduling';
@@ -23,7 +23,6 @@ export class ChangeDetectionSchedulerImpl implements ChangeDetectionScheduler {
2323
private taskService = inject(PendingTasks);
2424
private pendingRenderTaskId: number|null = null;
2525
private shouldRefreshViews = false;
26-
private readonly schedule = getCallbackScheduler();
2726
private readonly ngZone = inject(NgZone);
2827
private runningTick = false;
2928
private cancelScheduledCallback: null|(() => void) = null;
@@ -56,12 +55,12 @@ export class ChangeDetectionSchedulerImpl implements ChangeDetectionScheduler {
5655
// effectively get the unpatched versions of setTimeout and rAF (#55092)
5756
if (typeof Zone !== 'undefined' && Zone.root?.run) {
5857
Zone.root.run(() => {
59-
this.cancelScheduledCallback = this.schedule(() => {
58+
this.cancelScheduledCallback = scheduleCallback(() => {
6059
this.tick(this.shouldRefreshViews);
6160
});
6261
});
6362
} else {
64-
this.cancelScheduledCallback = this.schedule(() => {
63+
this.cancelScheduledCallback = scheduleCallback(() => {
6564
this.tick(this.shouldRefreshViews);
6665
});
6766
}

packages/core/src/util/callback_scheduler.ts

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import {global} from './global';
3434
*
3535
* @returns a function to cancel the scheduled callback
3636
*/
37-
export function getCallbackScheduler(): (callback: Function) => () => void {
37+
export function scheduleCallback(callback: Function): () => void {
3838
// Note: the `getNativeRequestAnimationFrame` is used in the `NgZone` class, but we cannot use the
3939
// `inject` function. The `NgZone` instance may be created manually, and thus the injection
4040
// context will be unavailable. This might be enough to check whether `requestAnimationFrame` is
@@ -60,25 +60,23 @@ export function getCallbackScheduler(): (callback: Function) => () => void {
6060
nativeSetTimeout = (nativeSetTimeout as any)[ORIGINAL_DELEGATE_SYMBOL] ?? nativeSetTimeout;
6161
}
6262

63-
return (callback: Function) => {
64-
let executeCallback = true;
65-
nativeSetTimeout(() => {
66-
if (!executeCallback) {
67-
return;
68-
}
69-
executeCallback = false;
70-
callback();
71-
});
72-
nativeRequestAnimationFrame?.(() => {
73-
if (!executeCallback) {
74-
return;
75-
}
76-
executeCallback = false;
77-
callback();
78-
});
63+
let executeCallback = true;
64+
nativeSetTimeout(() => {
65+
if (!executeCallback) {
66+
return;
67+
}
68+
executeCallback = false;
69+
callback();
70+
});
71+
nativeRequestAnimationFrame?.(() => {
72+
if (!executeCallback) {
73+
return;
74+
}
75+
executeCallback = false;
76+
callback();
77+
});
7978

80-
return () => {
81-
executeCallback = false;
82-
};
79+
return () => {
80+
executeCallback = false;
8381
};
8482
}

packages/core/src/zone/ng_zone.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
import {RuntimeError, RuntimeErrorCode} from '../errors';
1111
import {EventEmitter} from '../event_emitter';
12-
import {getCallbackScheduler} from '../util/callback_scheduler';
12+
import {scheduleCallback} from '../util/callback_scheduler';
1313
import {global} from '../util/global';
1414
import {noop} from '../util/noop';
1515

@@ -165,7 +165,7 @@ export class NgZone {
165165
!shouldCoalesceRunChangeDetection && shouldCoalesceEventChangeDetection;
166166
self.shouldCoalesceRunChangeDetection = shouldCoalesceRunChangeDetection;
167167
self.callbackScheduled = false;
168-
self.scheduleCallback = getCallbackScheduler();
168+
self.scheduleCallback = scheduleCallback;
169169
forkInnerZoneWithAngularBehavior(self);
170170
}
171171

packages/core/test/bundling/animations-standalone/bundle.golden_symbols.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -899,9 +899,6 @@
899899
{
900900
"name": "getBindingsEnabled"
901901
},
902-
{
903-
"name": "getCallbackScheduler"
904-
},
905902
{
906903
"name": "getClosureSafeProperty"
907904
},
@@ -1346,6 +1343,9 @@
13461343
{
13471344
"name": "saveNameToExportMap"
13481345
},
1346+
{
1347+
"name": "scheduleCallback"
1348+
},
13491349
{
13501350
"name": "searchTokensOnInjector"
13511351
},

packages/core/test/bundling/animations/bundle.golden_symbols.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -962,9 +962,6 @@
962962
{
963963
"name": "getBindingsEnabled"
964964
},
965-
{
966-
"name": "getCallbackScheduler"
967-
},
968965
{
969966
"name": "getClosureSafeProperty"
970967
},
@@ -1415,6 +1412,9 @@
14151412
{
14161413
"name": "saveNameToExportMap"
14171414
},
1415+
{
1416+
"name": "scheduleCallback"
1417+
},
14181418
{
14191419
"name": "searchTokensOnInjector"
14201420
},

packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -734,9 +734,6 @@
734734
{
735735
"name": "getBindingsEnabled"
736736
},
737-
{
738-
"name": "getCallbackScheduler"
739-
},
740737
{
741738
"name": "getClosureSafeProperty"
742739
},
@@ -1124,6 +1121,9 @@
11241121
{
11251122
"name": "saveNameToExportMap"
11261123
},
1124+
{
1125+
"name": "scheduleCallback"
1126+
},
11271127
{
11281128
"name": "searchTokensOnInjector"
11291129
},

packages/core/test/bundling/defer/bundle.golden_symbols.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -836,9 +836,6 @@
836836
{
837837
"name": "getBindingsEnabled"
838838
},
839-
{
840-
"name": "getCallbackScheduler"
841-
},
842839
{
843840
"name": "getClosureSafeProperty"
844841
},
@@ -2288,6 +2285,9 @@
22882285
{
22892286
"name": "saveResolvedLocalsInData"
22902287
},
2288+
{
2289+
"name": "scheduleCallback"
2290+
},
22912291
{
22922292
"name": "searchTokensOnInjector"
22932293
},

packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1022,9 +1022,6 @@
10221022
{
10231023
"name": "getBindingsEnabled"
10241024
},
1025-
{
1026-
"name": "getCallbackScheduler"
1027-
},
10281025
{
10291026
"name": "getClosureSafeProperty"
10301027
},
@@ -1631,6 +1628,9 @@
16311628
{
16321629
"name": "saveResolvedLocalsInData"
16331630
},
1631+
{
1632+
"name": "scheduleCallback"
1633+
},
16341634
{
16351635
"name": "searchTokensOnInjector"
16361636
},

packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -992,9 +992,6 @@
992992
{
993993
"name": "getBindingsEnabled"
994994
},
995-
{
996-
"name": "getCallbackScheduler"
997-
},
998995
{
999996
"name": "getClosureSafeProperty"
1000997
},
@@ -1616,6 +1613,9 @@
16161613
{
16171614
"name": "saveResolvedLocalsInData"
16181615
},
1616+
{
1617+
"name": "scheduleCallback"
1618+
},
16191619
{
16201620
"name": "searchTokensOnInjector"
16211621
},

packages/core/test/bundling/hello_world/bundle.golden_symbols.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -578,9 +578,6 @@
578578
{
579579
"name": "generateInitialInputs"
580580
},
581-
{
582-
"name": "getCallbackScheduler"
583-
},
584581
{
585582
"name": "getClosureSafeProperty"
586583
},
@@ -893,6 +890,9 @@
893890
{
894891
"name": "saveNameToExportMap"
895892
},
893+
{
894+
"name": "scheduleCallback"
895+
},
896896
{
897897
"name": "searchTokensOnInjector"
898898
},

0 commit comments

Comments
 (0)