|
6 | 6 | * found in the LICENSE file at https://angular.io/license |
7 | 7 | */ |
8 | 8 |
|
| 9 | +import {Subscription} from 'rxjs'; |
| 10 | + |
9 | 11 | import {ApplicationRef} from '../../application/application_ref'; |
10 | 12 | import {Injectable} from '../../di/injectable'; |
11 | 13 | import {inject} from '../../di/injector_compatibility'; |
@@ -43,29 +45,40 @@ function trackMicrotaskNotificationForDebugging() { |
43 | 45 |
|
44 | 46 | @Injectable({providedIn: 'root'}) |
45 | 47 | export class ChangeDetectionSchedulerImpl implements ChangeDetectionScheduler { |
46 | | - private appRef = inject(ApplicationRef); |
47 | | - private taskService = inject(PendingTasks); |
48 | | - private pendingRenderTaskId: number|null = null; |
49 | | - private shouldRefreshViews = false; |
| 48 | + private readonly appRef = inject(ApplicationRef); |
| 49 | + private readonly taskService = inject(PendingTasks); |
50 | 50 | private readonly ngZone = inject(NgZone); |
51 | | - runningTick = false; |
52 | | - private cancelScheduledCallback: null|(() => void) = null; |
53 | 51 | private readonly zonelessEnabled = inject(ZONELESS_ENABLED); |
54 | 52 | private readonly disableScheduling = |
55 | 53 | inject(ZONELESS_SCHEDULER_DISABLED, {optional: true}) ?? false; |
56 | 54 | private readonly zoneIsDefined = typeof Zone !== 'undefined' && !!Zone.root.run; |
57 | 55 | private readonly schedulerTickApplyArgs = [{data: {'__scheduler_tick__': true}}]; |
58 | | - private readonly afterTickSubscription = this.appRef.afterTick.subscribe(() => { |
59 | | - // If the scheduler isn't running a tick but the application ticked, that means |
60 | | - // someone called ApplicationRef.tick manually. In this case, we should cancel |
61 | | - // any change detections that had been scheduled so we don't run an extra one. |
62 | | - if (!this.runningTick) { |
63 | | - this.cleanup(); |
64 | | - } |
65 | | - }); |
| 56 | + private readonly subscriptions = new Subscription(); |
| 57 | + |
| 58 | + private cancelScheduledCallback: null|(() => void) = null; |
| 59 | + private shouldRefreshViews = false; |
| 60 | + private pendingRenderTaskId: number|null = null; |
66 | 61 | private useMicrotaskScheduler = false; |
| 62 | + runningTick = false; |
67 | 63 |
|
68 | 64 | constructor() { |
| 65 | + this.subscriptions.add(this.appRef.afterTick.subscribe(() => { |
| 66 | + // If the scheduler isn't running a tick but the application ticked, that means |
| 67 | + // someone called ApplicationRef.tick manually. In this case, we should cancel |
| 68 | + // any change detections that had been scheduled so we don't run an extra one. |
| 69 | + if (!this.runningTick) { |
| 70 | + this.cleanup(); |
| 71 | + } |
| 72 | + })); |
| 73 | + this.subscriptions.add(this.ngZone.onUnstable.subscribe(() => { |
| 74 | + // If the zone becomes unstable when we're not running tick (this happens from the zone.run), |
| 75 | + // we should cancel any scheduled change detection here because at this point we |
| 76 | + // know that the zone will stabilize at some point and run change detection itself. |
| 77 | + if (!this.runningTick) { |
| 78 | + this.cleanup(); |
| 79 | + } |
| 80 | + })); |
| 81 | + |
69 | 82 | // TODO(atscott): These conditions will need to change when zoneless is the default |
70 | 83 | // Instead, they should flip to checking if ZoneJS scheduling is provided |
71 | 84 | this.disableScheduling ||= !this.zonelessEnabled && |
@@ -197,7 +210,7 @@ export class ChangeDetectionSchedulerImpl implements ChangeDetectionScheduler { |
197 | 210 | } |
198 | 211 |
|
199 | 212 | ngOnDestroy() { |
200 | | - this.afterTickSubscription.unsubscribe(); |
| 213 | + this.subscriptions.unsubscribe(); |
201 | 214 | this.cleanup(); |
202 | 215 | } |
203 | 216 |
|
|
0 commit comments