Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions goldens/public-api/core/index.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1223,10 +1223,10 @@ export class NgProbeToken {

// @public
export class NgZone {
constructor({ enableLongStackTrace, shouldCoalesceEventChangeDetection, shouldCoalesceRunChangeDetection, }: {
enableLongStackTrace?: boolean | undefined;
shouldCoalesceEventChangeDetection?: boolean | undefined;
shouldCoalesceRunChangeDetection?: boolean | undefined;
constructor(options: {
enableLongStackTrace?: boolean;
shouldCoalesceEventChangeDetection?: boolean;
shouldCoalesceRunChangeDetection?: boolean;
});
static assertInAngularZone(): void;
static assertNotInAngularZone(): void;
Expand Down
9 changes: 9 additions & 0 deletions packages/core/src/change_detection/scheduling/flags.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/

export const SCHEDULE_IN_ROOT_ZONE_DEFAULT = true;
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ import {
ChangeDetectionScheduler,
ZONELESS_SCHEDULER_DISABLED,
ZONELESS_ENABLED,
SCHEDULE_IN_ROOT_ZONE,
} from './zoneless_scheduling';
import {SCHEDULE_IN_ROOT_ZONE_DEFAULT} from './flags';

@Injectable({providedIn: 'root'})
export class NgZoneChangeDetectionScheduler {
Expand Down Expand Up @@ -75,11 +77,14 @@ export const PROVIDED_NG_ZONE = new InjectionToken<boolean>(
export function internalProvideZoneChangeDetection({
ngZoneFactory,
ignoreChangesOutsideZone,
scheduleInRootZone,
}: {
ngZoneFactory?: () => NgZone;
ignoreChangesOutsideZone?: boolean;
scheduleInRootZone?: boolean;
}): StaticProvider[] {
ngZoneFactory ??= () => new NgZone(getNgZoneOptions());
ngZoneFactory ??= () =>
new NgZone({...getNgZoneOptions(), scheduleInRootZone} as InternalNgZoneOptions);
return [
{provide: NgZone, useFactory: ngZoneFactory},
{
Expand Down Expand Up @@ -115,6 +120,10 @@ export function internalProvideZoneChangeDetection({
// Always disable scheduler whenever explicitly disabled, even if another place called
// `provideZoneChangeDetection` without the 'ignore' option.
ignoreChangesOutsideZone === true ? {provide: ZONELESS_SCHEDULER_DISABLED, useValue: true} : [],
{
provide: SCHEDULE_IN_ROOT_ZONE,
useValue: scheduleInRootZone ?? SCHEDULE_IN_ROOT_ZONE_DEFAULT,
},
];
}

Expand All @@ -140,15 +149,18 @@ export function internalProvideZoneChangeDetection({
*/
export function provideZoneChangeDetection(options?: NgZoneOptions): EnvironmentProviders {
const ignoreChangesOutsideZone = options?.ignoreChangesOutsideZone;
const scheduleInRootZone = (options as any)?.scheduleInRootZone;
const zoneProviders = internalProvideZoneChangeDetection({
ngZoneFactory: () => {
const ngZoneOptions = getNgZoneOptions(options);
ngZoneOptions.scheduleInRootZone = scheduleInRootZone;
if (ngZoneOptions.shouldCoalesceEventChangeDetection) {
performanceMarkFeature('NgZone_CoalesceEvent');
}
return new NgZone(ngZoneOptions);
},
ignoreChangesOutsideZone,
scheduleInRootZone,
});
return makeEnvironmentProviders([
{provide: PROVIDED_NG_ZONE, useValue: true},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,8 @@ export const PROVIDED_ZONELESS = new InjectionToken<boolean>(
export const ZONELESS_SCHEDULER_DISABLED = new InjectionToken<boolean>(
typeof ngDevMode === 'undefined' || ngDevMode ? 'scheduler disabled' : '',
);

// TODO(atscott): Remove in v19. Scheduler should be done with runOutsideAngular.
export const SCHEDULE_IN_ROOT_ZONE = new InjectionToken<boolean>(
typeof ngDevMode === 'undefined' || ngDevMode ? 'run changes outside zone in root' : '',
);
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
ZONELESS_ENABLED,
PROVIDED_ZONELESS,
ZONELESS_SCHEDULER_DISABLED,
SCHEDULE_IN_ROOT_ZONE,
} from './zoneless_scheduling';

const CONSECUTIVE_MICROTASK_NOTIFICATION_LIMIT = 100;
Expand Down Expand Up @@ -67,6 +68,10 @@ export class ChangeDetectionSchedulerImpl implements ChangeDetectionScheduler {
private readonly angularZoneId = this.zoneIsDefined
? (this.ngZone as NgZonePrivate)._inner?.get(angularZoneInstanceIdProperty)
: null;
private readonly scheduleInRootZone =
!this.zonelessEnabled &&
this.zoneIsDefined &&
(inject(SCHEDULE_IN_ROOT_ZONE, {optional: true}) ?? false);

private cancelScheduledCallback: null | (() => void) = null;
private shouldRefreshViews = false;
Expand Down Expand Up @@ -156,16 +161,14 @@ export class ChangeDetectionSchedulerImpl implements ChangeDetectionScheduler {
? scheduleCallbackWithMicrotask
: scheduleCallbackWithRafRace;
this.pendingRenderTaskId = this.taskService.add();
if (this.zoneIsDefined) {
Zone.root.run(() => {
this.cancelScheduledCallback = scheduleCallback(() => {
this.tick(this.shouldRefreshViews);
});
});
if (this.scheduleInRootZone) {
this.cancelScheduledCallback = Zone.root.run(() =>
scheduleCallback(() => this.tick(this.shouldRefreshViews)),
);
} else {
this.cancelScheduledCallback = scheduleCallback(() => {
this.tick(this.shouldRefreshViews);
});
this.cancelScheduledCallback = this.ngZone.runOutsideAngular(() =>
scheduleCallback(() => this.tick(this.shouldRefreshViews)),
);
}
}

Expand Down Expand Up @@ -316,6 +319,7 @@ export function provideExperimentalZonelessChangeDetection(): EnvironmentProvide
{provide: ChangeDetectionScheduler, useExisting: ChangeDetectionSchedulerImpl},
{provide: NgZone, useClass: NoopNgZone},
{provide: ZONELESS_ENABLED, useValue: true},
{provide: SCHEDULE_IN_ROOT_ZONE, useValue: false},
typeof ngDevMode === 'undefined' || ngDevMode
? [{provide: PROVIDED_ZONELESS, useValue: true}]
: [],
Expand Down
9 changes: 5 additions & 4 deletions packages/core/src/platform/platform_ref.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,15 @@ export class PlatformRef {
moduleFactory: NgModuleFactory<M>,
options?: BootstrapOptions,
): Promise<NgModuleRef<M>> {
const scheduleInRootZone = (options as any)?.scheduleInRootZone;
const ngZoneFactory = () =>
getNgZone(
options?.ngZone,
getNgZoneOptions({
getNgZone(options?.ngZone, {
...getNgZoneOptions({
eventCoalescing: options?.ngZoneEventCoalescing,
runCoalescing: options?.ngZoneRunCoalescing,
}),
);
scheduleInRootZone,
});
const ignoreChangesOutsideZone = options?.ignoreChangesOutsideZone;
const allAppProviders = [
internalProvideZoneChangeDetection({
Expand Down
43 changes: 33 additions & 10 deletions packages/core/src/zone/ng_zone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
* found in the LICENSE file at https://angular.io/license
*/

import {SCHEDULE_IN_ROOT_ZONE_DEFAULT} from '../change_detection/scheduling/flags';
import {RuntimeError, RuntimeErrorCode} from '../errors';
import {EventEmitter} from '../event_emitter';
import {scheduleCallbackWithRafRace} from '../util/callback_scheduler';
import {global} from '../util/global';
import {noop} from '../util/noop';

import {AsyncStackTaggingZoneSpec} from './async-stack-tagging';
Expand Down Expand Up @@ -130,11 +130,18 @@ export class NgZone {
*/
readonly onError: EventEmitter<any> = new EventEmitter(false);

constructor({
enableLongStackTrace = false,
shouldCoalesceEventChangeDetection = false,
shouldCoalesceRunChangeDetection = false,
constructor(options: {
enableLongStackTrace?: boolean;
shouldCoalesceEventChangeDetection?: boolean;
shouldCoalesceRunChangeDetection?: boolean;
}) {
const {
enableLongStackTrace = false,
shouldCoalesceEventChangeDetection = false,
shouldCoalesceRunChangeDetection = false,
scheduleInRootZone = SCHEDULE_IN_ROOT_ZONE_DEFAULT,
} = options as InternalNgZoneOptions;

if (typeof Zone == 'undefined') {
throw new RuntimeError(
RuntimeErrorCode.MISSING_ZONEJS,
Expand Down Expand Up @@ -170,6 +177,7 @@ export class NgZone {
!shouldCoalesceRunChangeDetection && shouldCoalesceEventChangeDetection;
self.shouldCoalesceRunChangeDetection = shouldCoalesceRunChangeDetection;
self.callbackScheduled = false;
self.scheduleInRootZone = scheduleInRootZone;
forkInnerZoneWithAngularBehavior(self);
}

Expand Down Expand Up @@ -330,6 +338,11 @@ export interface NgZonePrivate extends NgZone {
*
*/
shouldCoalesceRunChangeDetection: boolean;

/**
* Whether to schedule the coalesced change detection in the root zone
*/
scheduleInRootZone: boolean;
}

function checkStable(zone: NgZonePrivate) {
Expand Down Expand Up @@ -383,15 +396,24 @@ function delayChangeDetectionForEvents(zone: NgZonePrivate) {
return;
}
zone.callbackScheduled = true;
Zone.root.run(() => {
function scheduleCheckStable() {
scheduleCallbackWithRafRace(() => {
zone.callbackScheduled = false;
updateMicroTaskStatus(zone);
zone.isCheckStableRunning = true;
checkStable(zone);
zone.isCheckStableRunning = false;
});
});
}
if (zone.scheduleInRootZone) {
Zone.root.run(() => {
scheduleCheckStable();
});
} else {
zone._outer.run(() => {
scheduleCheckStable();
});
}
updateMicroTaskStatus(zone);
}

Expand Down Expand Up @@ -574,9 +596,10 @@ function hasApplyArgsData(applyArgs: unknown, key: string) {

// Set of options recognized by the NgZone.
export interface InternalNgZoneOptions {
enableLongStackTrace: boolean;
shouldCoalesceEventChangeDetection: boolean;
shouldCoalesceRunChangeDetection: boolean;
enableLongStackTrace?: boolean;
shouldCoalesceEventChangeDetection?: boolean;
shouldCoalesceRunChangeDetection?: boolean;
scheduleInRootZone?: boolean;
}

export function getNgZone(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,9 @@
{
"name": "RuntimeError"
},
{
"name": "SCHEDULE_IN_ROOT_ZONE"
},
{
"name": "SELF_TOKEN_REGEX"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,9 @@
{
"name": "RuntimeError"
},
{
"name": "SCHEDULE_IN_ROOT_ZONE"
},
{
"name": "SELF_TOKEN_REGEX"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,9 @@
{
"name": "RuntimeError"
},
{
"name": "SCHEDULE_IN_ROOT_ZONE"
},
{
"name": "SIGNAL"
},
Expand Down
6 changes: 6 additions & 0 deletions packages/core/test/bundling/defer/bundle.golden_symbols.json
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,9 @@
{
"name": "RuntimeError"
},
{
"name": "SCHEDULE_IN_ROOT_ZONE"
},
{
"name": "SIGNAL"
},
Expand Down Expand Up @@ -1460,6 +1463,9 @@
{
"name": "init_fields"
},
{
"name": "init_flags"
},
{
"name": "init_forward_ref"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,9 @@
{
"name": "RuntimeError"
},
{
"name": "SCHEDULE_IN_ROOT_ZONE"
},
{
"name": "SIGNAL"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,9 @@
{
"name": "RuntimeError"
},
{
"name": "SCHEDULE_IN_ROOT_ZONE"
},
{
"name": "SIGNAL"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,9 @@
{
"name": "RuntimeError"
},
{
"name": "SCHEDULE_IN_ROOT_ZONE"
},
{
"name": "SIGNAL"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,9 @@
{
"name": "RuntimeError"
},
{
"name": "SCHEDULE_IN_ROOT_ZONE"
},
{
"name": "SIGNAL"
},
Expand Down
3 changes: 3 additions & 0 deletions packages/core/test/bundling/router/bundle.golden_symbols.json
Original file line number Diff line number Diff line change
Expand Up @@ -644,6 +644,9 @@
{
"name": "SAFE_URL_PATTERN"
},
{
"name": "SCHEDULE_IN_ROOT_ZONE"
},
{
"name": "SEGMENT_RE"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,9 @@
{
"name": "RuntimeError"
},
{
"name": "SCHEDULE_IN_ROOT_ZONE"
},
{
"name": "SIGNAL"
},
Expand Down
3 changes: 3 additions & 0 deletions packages/core/test/bundling/todo/bundle.golden_symbols.json
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,9 @@
{
"name": "RuntimeError"
},
{
"name": "SCHEDULE_IN_ROOT_ZONE"
},
{
"name": "SIGNAL"
},
Expand Down
Loading