mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
refactor(core): use patched timers in root zone for zoneless scheduler (#55367)
Rather than attempting to use the native timing functions, this commit simplifies the logic significantly by using the global timer functions as they are, either patched or unpatched. When Zone is defined, we run the timers in the root zone. This has more predictable behavior and timing than (a) using both patched and unpatched versions of timers in different places (b) trying to get an unpatched timer and failing due to environment specifics and patches that aren't ZoneJS. We will try to update the coalescing behavior of ZoneJS in a future PR to also just use the patched version of the timers instead of the "fakeTopEvent" workaround with the native timers. PR Close #55367
This commit is contained in:
parent
f09c5a7bc4
commit
e6425f7bc1
2 changed files with 12 additions and 12 deletions
|
|
@ -30,7 +30,7 @@ export class ChangeDetectionSchedulerImpl implements ChangeDetectionScheduler {
|
|||
private readonly zonelessEnabled = inject(ZONELESS_ENABLED);
|
||||
private readonly disableScheduling =
|
||||
inject(ZONELESS_SCHEDULER_DISABLED, {optional: true}) ?? false;
|
||||
private readonly zoneIsDefined = typeof Zone !== 'undefined' && !!Zone.root.scheduleEventTask;
|
||||
private readonly zoneIsDefined = typeof Zone !== 'undefined' && !!Zone.root.run;
|
||||
private readonly afterTickSubscription = this.appRef.afterTick.subscribe(() => {
|
||||
// If the scheduler isn't running a tick but the application ticked, that means
|
||||
// someone called ApplicationRef.tick manually. In this case, we should cancel
|
||||
|
|
@ -60,17 +60,17 @@ export class ChangeDetectionSchedulerImpl implements ChangeDetectionScheduler {
|
|||
}
|
||||
|
||||
this.pendingRenderTaskId = this.taskService.add();
|
||||
this.cancelScheduledCallback = scheduleCallback(() => {
|
||||
if (this.zoneIsDefined) {
|
||||
// https://github.com/angular/angular/blob/c9abe775d07d075b171a187844d09e57f9685f3b/packages/core/src/zone/ng_zone.ts#L387-L395
|
||||
const task = Zone.root.scheduleEventTask('fakeTopEventTask', () => {
|
||||
if (this.zoneIsDefined) {
|
||||
Zone.root.run(() => {
|
||||
this.cancelScheduledCallback = scheduleCallback(() => {
|
||||
this.tick(this.shouldRefreshViews);
|
||||
}, undefined, () => {}, () => {});
|
||||
task.invoke();
|
||||
} else {
|
||||
}, false /** useNativeTimers */);
|
||||
});
|
||||
} else {
|
||||
this.cancelScheduledCallback = scheduleCallback(() => {
|
||||
this.tick(this.shouldRefreshViews);
|
||||
}
|
||||
});
|
||||
}, false /** useNativeTimers */);
|
||||
}
|
||||
}
|
||||
|
||||
private shouldScheduleTick(): boolean {
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ import {global} from './global';
|
|||
*
|
||||
* @returns a function to cancel the scheduled callback
|
||||
*/
|
||||
export function scheduleCallback(callback: Function): () => void {
|
||||
export function scheduleCallback(callback: Function, useNativeTimers = true): () => void {
|
||||
// Note: the `scheduleCallback` is used in the `NgZone` class, but we cannot use the
|
||||
// `inject` function. The `NgZone` instance may be created manually, and thus the injection
|
||||
// context will be unavailable. This might be enough to check whether `requestAnimationFrame` is
|
||||
|
|
@ -43,7 +43,7 @@ export function scheduleCallback(callback: Function): () => void {
|
|||
let nativeRequestAnimationFrame =
|
||||
hasRequestAnimationFrame ? global['requestAnimationFrame'] : null;
|
||||
let nativeSetTimeout = global['setTimeout'];
|
||||
if (typeof Zone !== 'undefined') {
|
||||
if (typeof Zone !== 'undefined' && useNativeTimers) {
|
||||
if (hasRequestAnimationFrame) {
|
||||
nativeRequestAnimationFrame =
|
||||
global[Zone.__symbol__('requestAnimationFrame')] ?? nativeRequestAnimationFrame;
|
||||
|
|
|
|||
Loading…
Reference in a new issue