fix(core): execute timer trigger outside zone (#60392)

This should prevent defer timers from impacting app stability by executing them outside of the zone, similar to other defer triggers.

fixes: #60373

PR Close #60392
This commit is contained in:
Jessica Janiuk 2025-03-14 10:28:15 -04:00 committed by Pawel Kozlowski
parent 24c5398f7d
commit 296aded9da

View file

@ -8,6 +8,7 @@
import {Injector, ɵɵdefineInjectable} from '../di';
import {arrayInsert2, arraySplice} from '../util/array_utils';
import {NgZone} from '../zone';
/**
* Returns a function that captures a provided delay.
@ -27,8 +28,9 @@ export function onTimer(delay: number) {
*/
export function scheduleTimerTrigger(delay: number, callback: VoidFunction, injector: Injector) {
const scheduler = injector.get(TimerScheduler);
const ngZone = injector.get(NgZone);
const cleanupFn = () => scheduler.remove(callback);
scheduler.add(delay, callback);
scheduler.add(delay, callback, ngZone);
return cleanupFn;
}
@ -60,10 +62,10 @@ export class TimerScheduler {
// as the shape of the `current` list.
deferred: Array<number | VoidFunction> = [];
add(delay: number, callback: VoidFunction) {
add(delay: number, callback: VoidFunction, ngZone: NgZone) {
const target = this.executingCallbacks ? this.deferred : this.current;
this.addToQueue(target, Date.now() + delay, callback);
this.scheduleTimer();
this.scheduleTimer(ngZone);
}
remove(callback: VoidFunction) {
@ -117,7 +119,7 @@ export class TimerScheduler {
return index;
}
private scheduleTimer() {
private scheduleTimer(ngZone: NgZone) {
const callback = () => {
this.clearTimeout();
@ -170,7 +172,7 @@ export class TimerScheduler {
}
this.deferred.length = 0;
}
this.scheduleTimer();
this.scheduleTimer(ngZone);
};
// Avoid running timer callbacks more than once per
@ -198,7 +200,9 @@ export class TimerScheduler {
const timeout = Math.max(invokeAt - now, FRAME_DURATION_MS);
this.invokeTimerAt = invokeAt;
this.timeoutId = setTimeout(callback, timeout) as unknown as number;
this.timeoutId = ngZone.runOutsideAngular(() => {
return setTimeout(() => ngZone.run(callback), timeout) as unknown as number;
});
}
}
}