@angular/core 17.3.1 → 18.0.0-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/esm2022/src/application/application_ref.mjs +14 -29
  2. package/esm2022/src/change_detection/flags.mjs +4 -3
  3. package/esm2022/src/change_detection/scheduling/zoneless_scheduling_impl.mjs +6 -34
  4. package/esm2022/src/render3/component_ref.mjs +1 -1
  5. package/esm2022/src/render3/instructions/change_detection.mjs +26 -18
  6. package/esm2022/src/render3/instructions/mark_view_dirty.mjs +11 -2
  7. package/esm2022/src/render3/state.mjs +13 -1
  8. package/esm2022/src/util/callback_scheduler.mjs +74 -0
  9. package/esm2022/src/version.mjs +1 -1
  10. package/esm2022/src/zone/ng_zone.mjs +9 -8
  11. package/esm2022/testing/src/component_fixture.mjs +46 -11
  12. package/esm2022/testing/src/logger.mjs +3 -3
  13. package/fesm2022/core.mjs +133 -113
  14. package/fesm2022/core.mjs.map +1 -1
  15. package/fesm2022/primitives/signals.mjs +1 -1
  16. package/fesm2022/rxjs-interop.mjs +1 -1
  17. package/fesm2022/testing.mjs +46 -11
  18. package/fesm2022/testing.mjs.map +1 -1
  19. package/index.d.ts +1 -1
  20. package/package.json +1 -1
  21. package/primitives/signals/index.d.ts +1 -1
  22. package/rxjs-interop/index.d.ts +1 -1
  23. package/schematics/migrations/block-template-entities/bundle.js +6542 -10011
  24. package/schematics/migrations/block-template-entities/bundle.js.map +4 -4
  25. package/schematics/migrations/invalid-two-way-bindings/bundle.js +9282 -12751
  26. package/schematics/migrations/invalid-two-way-bindings/bundle.js.map +4 -4
  27. package/schematics/ng-generate/control-flow-migration/bundle.js +6553 -10022
  28. package/schematics/ng-generate/control-flow-migration/bundle.js.map +4 -4
  29. package/schematics/ng-generate/standalone-migration/bundle.js +14133 -16201
  30. package/schematics/ng-generate/standalone-migration/bundle.js.map +4 -4
  31. package/testing/index.d.ts +1 -1
  32. package/esm2022/src/util/raf.mjs +0 -41
package/fesm2022/core.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Angular v17.3.1
2
+ * @license Angular v18.0.0-next.1
3
3
  * (c) 2010-2022 Google LLC. https://angular.io/
4
4
  * License: MIT
5
5
  */
@@ -4152,8 +4152,9 @@ const profiler = function (event, instance, hookOrListener) {
4152
4152
  const SVG_NAMESPACE = 'svg';
4153
4153
  const MATH_ML_NAMESPACE = 'math';
4154
4154
 
4155
- // TODO(atscott): flip default internally ASAP and externally for v18 (#52928)
4156
- let _ensureDirtyViewsAreAlwaysReachable = false;
4155
+ // TODO(atscott): Remove prior to v18 release. Keeping this around in case anyone internally needs
4156
+ // to opt out temporarily.
4157
+ let _ensureDirtyViewsAreAlwaysReachable = true;
4157
4158
  function getEnsureDirtyViewsAreAlwaysReachable() {
4158
4159
  return _ensureDirtyViewsAreAlwaysReachable;
4159
4160
  }
@@ -4415,6 +4416,12 @@ const instructionState = {
4415
4416
  * changes exist in the change detector or its children.
4416
4417
  */
4417
4418
  let _isInCheckNoChangesMode = false;
4419
+ /**
4420
+ * Flag used to indicate that we are in the middle running change detection on a view
4421
+ *
4422
+ * @see detectChangesInViewWhileDirty
4423
+ */
4424
+ let _isRefreshingViews = false;
4418
4425
  /**
4419
4426
  * Returns true if the instruction state stack is empty.
4420
4427
  *
@@ -4585,6 +4592,12 @@ function setIsInCheckNoChangesMode(mode) {
4585
4592
  !ngDevMode && throwError('Must never be called in production mode');
4586
4593
  _isInCheckNoChangesMode = mode;
4587
4594
  }
4595
+ function isRefreshingViews() {
4596
+ return _isRefreshingViews;
4597
+ }
4598
+ function setIsRefreshingViews(mode) {
4599
+ _isRefreshingViews = mode;
4600
+ }
4588
4601
  // top level variables should not be exported for performance reasons (PERF_NOTES.md)
4589
4602
  function getBindingRoot() {
4590
4603
  const lFrame = instructionState.lFrame;
@@ -12654,7 +12667,7 @@ const REACTIVE_LVIEW_CONSUMER_NODE = {
12654
12667
  /**
12655
12668
  * The maximum number of times the change detection traversal will rerun before throwing an error.
12656
12669
  */
12657
- const MAXIMUM_REFRESH_RERUNS = 100;
12670
+ const MAXIMUM_REFRESH_RERUNS$1 = 100;
12658
12671
  function detectChangesInternal(lView, notifyErrorHandler = true, mode = 0 /* ChangeDetectionMode.Global */) {
12659
12672
  const environment = lView[ENVIRONMENT];
12660
12673
  const rendererFactory = environment.rendererFactory;
@@ -12684,23 +12697,31 @@ function detectChangesInternal(lView, notifyErrorHandler = true, mode = 0 /* Cha
12684
12697
  }
12685
12698
  }
12686
12699
  function detectChangesInViewWhileDirty(lView, mode) {
12687
- detectChangesInView$1(lView, mode);
12688
- let retries = 0;
12689
- // If after running change detection, this view still needs to be refreshed or there are
12690
- // descendants views that need to be refreshed due to re-dirtying during the change detection
12691
- // run, detect changes on the view again. We run change detection in `Targeted` mode to only
12692
- // refresh views with the `RefreshView` flag.
12693
- while (requiresRefreshOrTraversal(lView)) {
12694
- if (retries === MAXIMUM_REFRESH_RERUNS) {
12695
- throw new RuntimeError(103 /* RuntimeErrorCode.INFINITE_CHANGE_DETECTION */, ngDevMode &&
12696
- 'Infinite change detection while trying to refresh views. ' +
12697
- 'There may be components which each cause the other to require a refresh, ' +
12698
- 'causing an infinite loop.');
12700
+ const lastIsRefreshingViewsValue = isRefreshingViews();
12701
+ try {
12702
+ setIsRefreshingViews(true);
12703
+ detectChangesInView$1(lView, mode);
12704
+ let retries = 0;
12705
+ // If after running change detection, this view still needs to be refreshed or there are
12706
+ // descendants views that need to be refreshed due to re-dirtying during the change detection
12707
+ // run, detect changes on the view again. We run change detection in `Targeted` mode to only
12708
+ // refresh views with the `RefreshView` flag.
12709
+ while (requiresRefreshOrTraversal(lView)) {
12710
+ if (retries === MAXIMUM_REFRESH_RERUNS$1) {
12711
+ throw new RuntimeError(103 /* RuntimeErrorCode.INFINITE_CHANGE_DETECTION */, ngDevMode &&
12712
+ 'Infinite change detection while trying to refresh views. ' +
12713
+ 'There may be components which each cause the other to require a refresh, ' +
12714
+ 'causing an infinite loop.');
12715
+ }
12716
+ retries++;
12717
+ // Even if this view is detached, we still detect changes in targeted mode because this was
12718
+ // the root of the change detection run.
12719
+ detectChangesInView$1(lView, 1 /* ChangeDetectionMode.Targeted */);
12699
12720
  }
12700
- retries++;
12701
- // Even if this view is detached, we still detect changes in targeted mode because this was
12702
- // the root of the change detection run.
12703
- detectChangesInView$1(lView, 1 /* ChangeDetectionMode.Targeted */);
12721
+ }
12722
+ finally {
12723
+ // restore state to what it was before entering this change detection loop
12724
+ setIsRefreshingViews(lastIsRefreshingViewsValue);
12704
12725
  }
12705
12726
  }
12706
12727
  function checkNoChangesInternal(lView, notifyErrorHandler = true) {
@@ -12996,9 +13017,17 @@ function detectChangesInChildComponents(hostLView, components, mode) {
12996
13017
  * @returns the root LView
12997
13018
  */
12998
13019
  function markViewDirty(lView) {
13020
+ const dirtyBitsToUse = isRefreshingViews() ?
13021
+ 64 /* LViewFlags.Dirty */ :
13022
+ // When we are not actively refreshing a view tree, it is absolutely
13023
+ // valid to update state and mark views dirty. We use the `RefreshView` flag in this
13024
+ // case to allow synchronously rerunning change detection. This applies today to
13025
+ // afterRender hooks as well as animation listeners which execute after detecting
13026
+ // changes in a view when the render factory flushes.
13027
+ 1024 /* LViewFlags.RefreshView */ | 64 /* LViewFlags.Dirty */;
12999
13028
  lView[ENVIRONMENT].changeDetectionScheduler?.notify();
13000
13029
  while (lView) {
13001
- lView[FLAGS] |= 64 /* LViewFlags.Dirty */;
13030
+ lView[FLAGS] |= dirtyBitsToUse;
13002
13031
  const parent = getLViewParent(lView);
13003
13032
  // Stop traversing up as soon as you find a root view that wasn't attached to any container
13004
13033
  if (isRootView(lView) && !parent) {
@@ -14459,41 +14488,74 @@ function performanceMarkFeature(feature) {
14459
14488
  performance?.mark?.('mark_feature_usage', { detail: { feature } });
14460
14489
  }
14461
14490
 
14462
- function noop(...args) {
14463
- // Do nothing.
14464
- }
14465
-
14466
- function getNativeRequestAnimationFrame() {
14491
+ /**
14492
+ * Gets a scheduling function that runs the callback after the first of setTimeout and
14493
+ * requestAnimationFrame resolves.
14494
+ *
14495
+ * - `requestAnimationFrame` ensures that change detection runs ahead of a browser repaint.
14496
+ * This ensures that the create and update passes of a change detection always happen
14497
+ * in the same frame.
14498
+ * - When the browser is resource-starved, `rAF` can execute _before_ a `setTimeout` because
14499
+ * rendering is a very high priority process. This means that `setTimeout` cannot guarantee
14500
+ * same-frame create and update pass, when `setTimeout` is used to schedule the update phase.
14501
+ * - While `rAF` gives us the desirable same-frame updates, it has two limitations that
14502
+ * prevent it from being used alone. First, it does not run in background tabs, which would
14503
+ * prevent Angular from initializing an application when opened in a new tab (for example).
14504
+ * Second, repeated calls to requestAnimationFrame will execute at the refresh rate of the
14505
+ * hardware (~16ms for a 60Hz display). This would cause significant slowdown of tests that
14506
+ * are written with several updates and asserts in the form of "update; await stable; assert;".
14507
+ * - Both `setTimeout` and `rAF` are able to "coalesce" several events from a single user
14508
+ * interaction into a single change detection. Importantly, this reduces view tree traversals when
14509
+ * compared to an alternative timing mechanism like `queueMicrotask`, where change detection would
14510
+ * then be interleaves between each event.
14511
+ *
14512
+ * By running change detection after the first of `setTimeout` and `rAF` to execute, we get the
14513
+ * best of both worlds.
14514
+ */
14515
+ function getCallbackScheduler() {
14467
14516
  // Note: the `getNativeRequestAnimationFrame` is used in the `NgZone` class, but we cannot use the
14468
14517
  // `inject` function. The `NgZone` instance may be created manually, and thus the injection
14469
14518
  // context will be unavailable. This might be enough to check whether `requestAnimationFrame` is
14470
14519
  // available because otherwise, we'll fall back to `setTimeout`.
14471
- const isBrowser = typeof _global['requestAnimationFrame'] === 'function';
14472
- // Note: `requestAnimationFrame` is unavailable when the code runs in the Node.js environment. We
14473
- // use `setTimeout` because no changes are required other than checking if the current platform is
14474
- // the browser. `setTimeout` is a well-established API that is available in both environments.
14475
- // `requestAnimationFrame` is used in the browser to coalesce event tasks since event tasks are
14476
- // usually executed within the same rendering frame (but this is more implementation details of
14477
- // browsers).
14478
- let nativeRequestAnimationFrame = _global[isBrowser ? 'requestAnimationFrame' : 'setTimeout'];
14479
- let nativeCancelAnimationFrame = _global[isBrowser ? 'cancelAnimationFrame' : 'clearTimeout'];
14480
- if (typeof Zone !== 'undefined' && nativeRequestAnimationFrame && nativeCancelAnimationFrame) {
14520
+ const hasRequestAnimationFrame = typeof _global['requestAnimationFrame'] === 'function';
14521
+ let nativeRequestAnimationFrame = hasRequestAnimationFrame ? _global['requestAnimationFrame'] : null;
14522
+ let nativeSetTimeout = _global['setTimeout'];
14523
+ if (typeof Zone !== 'undefined') {
14481
14524
  // Note: zone.js sets original implementations on patched APIs behind the
14482
14525
  // `__zone_symbol__OriginalDelegate` key (see `attachOriginToPatched`). Given the following
14483
14526
  // example: `window.requestAnimationFrame.__zone_symbol__OriginalDelegate`; this would return an
14484
14527
  // unpatched implementation of the `requestAnimationFrame`, which isn't intercepted by the
14485
14528
  // Angular zone. We use the unpatched implementation to avoid another change detection when
14486
14529
  // coalescing tasks.
14487
- const unpatchedRequestAnimationFrame = nativeRequestAnimationFrame[Zone.__symbol__('OriginalDelegate')];
14488
- if (unpatchedRequestAnimationFrame) {
14489
- nativeRequestAnimationFrame = unpatchedRequestAnimationFrame;
14490
- }
14491
- const unpatchedCancelAnimationFrame = nativeCancelAnimationFrame[Zone.__symbol__('OriginalDelegate')];
14492
- if (unpatchedCancelAnimationFrame) {
14493
- nativeCancelAnimationFrame = unpatchedCancelAnimationFrame;
14494
- }
14495
- }
14496
- return { nativeRequestAnimationFrame, nativeCancelAnimationFrame };
14530
+ const ORIGINAL_DELEGATE_SYMBOL = Zone.__symbol__('OriginalDelegate');
14531
+ if (nativeRequestAnimationFrame) {
14532
+ nativeRequestAnimationFrame =
14533
+ nativeRequestAnimationFrame[ORIGINAL_DELEGATE_SYMBOL] ??
14534
+ nativeRequestAnimationFrame;
14535
+ }
14536
+ nativeSetTimeout = nativeSetTimeout[ORIGINAL_DELEGATE_SYMBOL] ?? nativeSetTimeout;
14537
+ }
14538
+ return (callback) => {
14539
+ let executeCallback = true;
14540
+ nativeSetTimeout(() => {
14541
+ if (!executeCallback) {
14542
+ return;
14543
+ }
14544
+ executeCallback = false;
14545
+ callback();
14546
+ });
14547
+ nativeRequestAnimationFrame?.(() => {
14548
+ if (!executeCallback) {
14549
+ return;
14550
+ }
14551
+ executeCallback = false;
14552
+ callback();
14553
+ });
14554
+ };
14555
+ }
14556
+
14557
+ function noop(...args) {
14558
+ // Do nothing.
14497
14559
  }
14498
14560
 
14499
14561
  class AsyncStackTaggingZoneSpec {
@@ -14645,8 +14707,8 @@ class NgZone {
14645
14707
  self.shouldCoalesceEventChangeDetection =
14646
14708
  !shouldCoalesceRunChangeDetection && shouldCoalesceEventChangeDetection;
14647
14709
  self.shouldCoalesceRunChangeDetection = shouldCoalesceRunChangeDetection;
14648
- self.lastRequestAnimationFrameId = -1;
14649
- self.nativeRequestAnimationFrame = getNativeRequestAnimationFrame().nativeRequestAnimationFrame;
14710
+ self.callbackScheduled = false;
14711
+ self.scheduleCallback = getCallbackScheduler();
14650
14712
  forkInnerZoneWithAngularBehavior(self);
14651
14713
  }
14652
14714
  /**
@@ -14782,10 +14844,11 @@ function delayChangeDetectionForEvents(zone) {
14782
14844
  * so we also need to check the _nesting here to prevent multiple
14783
14845
  * change detections.
14784
14846
  */
14785
- if (zone.isCheckStableRunning || zone.lastRequestAnimationFrameId !== -1) {
14847
+ if (zone.isCheckStableRunning || zone.callbackScheduled) {
14786
14848
  return;
14787
14849
  }
14788
- zone.lastRequestAnimationFrameId = zone.nativeRequestAnimationFrame.call(_global, () => {
14850
+ zone.callbackScheduled = true;
14851
+ zone.scheduleCallback.call(_global, () => {
14789
14852
  // This is a work around for https://github.com/angular/angular/issues/36839.
14790
14853
  // The core issue is that when event coalescing is enabled it is possible for microtasks
14791
14854
  // to get flushed too early (As is the case with `Promise.then`) between the
@@ -14797,7 +14860,7 @@ function delayChangeDetectionForEvents(zone) {
14797
14860
  // eventTask execution.
14798
14861
  if (!zone.fakeTopEventTask) {
14799
14862
  zone.fakeTopEventTask = Zone.root.scheduleEventTask('fakeTopEventTask', () => {
14800
- zone.lastRequestAnimationFrameId = -1;
14863
+ zone.callbackScheduled = false;
14801
14864
  updateMicroTaskStatus(zone);
14802
14865
  zone.isCheckStableRunning = true;
14803
14866
  checkStable(zone);
@@ -14868,7 +14931,7 @@ function forkInnerZoneWithAngularBehavior(zone) {
14868
14931
  function updateMicroTaskStatus(zone) {
14869
14932
  if (zone._hasPendingMicrotasks ||
14870
14933
  ((zone.shouldCoalesceEventChangeDetection || zone.shouldCoalesceRunChangeDetection) &&
14871
- zone.lastRequestAnimationFrameId !== -1)) {
14934
+ zone.callbackScheduled === true)) {
14872
14935
  zone.hasPendingMicrotasks = true;
14873
14936
  }
14874
14937
  else {
@@ -15977,7 +16040,7 @@ function createRootComponent(componentView, rootComponentDef, rootDirectives, ho
15977
16040
  function setRootNodeAttributes(hostRenderer, componentDef, hostRNode, rootSelectorOrNode) {
15978
16041
  if (rootSelectorOrNode) {
15979
16042
  // The placeholder will be replaced with the actual version at build time.
15980
- setUpAttributes(hostRenderer, hostRNode, ['ng-version', '17.3.1']);
16043
+ setUpAttributes(hostRenderer, hostRNode, ['ng-version', '18.0.0-next.1']);
15981
16044
  }
15982
16045
  else {
15983
16046
  // If host element is created as a part of this function call (i.e. `rootSelectorOrNode`
@@ -29741,7 +29804,7 @@ class Version {
29741
29804
  /**
29742
29805
  * @publicApi
29743
29806
  */
29744
- const VERSION = new Version('17.3.1');
29807
+ const VERSION = new Version('18.0.0-next.1');
29745
29808
 
29746
29809
  class Console {
29747
29810
  log(message) {
@@ -31144,6 +31207,8 @@ class NgProbeToken {
31144
31207
  this.token = token;
31145
31208
  }
31146
31209
  }
31210
+ /** Maximum number of times ApplicationRef will refresh all attached views in a single tick. */
31211
+ const MAXIMUM_REFRESH_RERUNS = 10;
31147
31212
  function _callAndReportToErrorHandler(errorHandler, ngZone, callback) {
31148
31213
  try {
31149
31214
  const result = callback();
@@ -31423,12 +31488,7 @@ class ApplicationRef {
31423
31488
  detectChangesInAttachedViews(refreshViews) {
31424
31489
  let runs = 0;
31425
31490
  const afterRenderEffectManager = this.afterRenderEffectManager;
31426
- while (true) {
31427
- if (runs === MAXIMUM_REFRESH_RERUNS) {
31428
- throw new RuntimeError(103 /* RuntimeErrorCode.INFINITE_CHANGE_DETECTION */, ngDevMode &&
31429
- 'Infinite change detection while refreshing application views. ' +
31430
- 'Ensure afterRender or queueStateUpdate hooks are not continuously causing updates.');
31431
- }
31491
+ while (runs < MAXIMUM_REFRESH_RERUNS) {
31432
31492
  if (refreshViews) {
31433
31493
  const isFirstPass = runs === 0;
31434
31494
  this.beforeRender.next(isFirstPass);
@@ -31450,6 +31510,12 @@ class ApplicationRef {
31450
31510
  break;
31451
31511
  }
31452
31512
  }
31513
+ if ((typeof ngDevMode === 'undefined' || ngDevMode) && runs >= MAXIMUM_REFRESH_RERUNS) {
31514
+ throw new RuntimeError(103 /* RuntimeErrorCode.INFINITE_CHANGE_DETECTION */, ngDevMode &&
31515
+ 'Infinite change detection while refreshing application views. ' +
31516
+ 'Ensure views are not calling `markForCheck` on every template execution or ' +
31517
+ 'that afterRender hooks always mark views for check.');
31518
+ }
31453
31519
  }
31454
31520
  /**
31455
31521
  * Attaches a view so that it will be dirty checked.
@@ -31584,27 +31650,9 @@ function shouldRecheckView(view) {
31584
31650
  return requiresRefreshOrTraversal(view);
31585
31651
  }
31586
31652
  function detectChangesInView(lView, notifyErrorHandler, isFirstPass) {
31587
- let mode;
31588
- if (isFirstPass) {
31589
- // The first pass is always in Global mode, which includes `CheckAlways` views.
31590
- mode = 0 /* ChangeDetectionMode.Global */;
31591
- // Add `RefreshView` flag to ensure this view is refreshed if not already dirty.
31592
- // `RefreshView` flag is used intentionally over `Dirty` because it gets cleared before
31593
- // executing any of the actual refresh code while the `Dirty` flag doesn't get cleared
31594
- // until the end of the refresh. Using `RefreshView` prevents creating a potential
31595
- // difference in the state of the LViewFlags during template execution.
31596
- lView[FLAGS] |= 1024 /* LViewFlags.RefreshView */;
31597
- }
31598
- else if (lView[FLAGS] & 64 /* LViewFlags.Dirty */) {
31599
- // The root view has been explicitly marked for check, so check it in Global mode.
31600
- mode = 0 /* ChangeDetectionMode.Global */;
31601
- }
31602
- else {
31603
- // The view has not been marked for check, but contains a view marked for refresh
31604
- // (likely via a signal). Start this change detection in Targeted mode to skip the root
31605
- // view and check just the view(s) that need refreshed.
31606
- mode = 1 /* ChangeDetectionMode.Targeted */;
31607
- }
31653
+ const mode = isFirstPass || lView[FLAGS] & 64 /* LViewFlags.Dirty */ ?
31654
+ 0 /* ChangeDetectionMode.Global */ :
31655
+ 1 /* ChangeDetectionMode.Targeted */;
31608
31656
  detectChangesInternal(lView, notifyErrorHandler, mode);
31609
31657
  }
31610
31658
 
@@ -35001,6 +35049,7 @@ class ChangeDetectionSchedulerImpl {
35001
35049
  this.taskService = inject(PendingTasks);
35002
35050
  this.pendingRenderTaskId = null;
35003
35051
  this.shouldRefreshViews = false;
35052
+ this.schedule = getCallbackScheduler();
35004
35053
  }
35005
35054
  notify(type = 0 /* NotificationType.RefreshViews */) {
35006
35055
  // When the only source of notification is an afterRender hook will skip straight to the hooks
@@ -35010,38 +35059,9 @@ class ChangeDetectionSchedulerImpl {
35010
35059
  return;
35011
35060
  }
35012
35061
  this.pendingRenderTaskId = this.taskService.add();
35013
- this.raceTimeoutAndRequestAnimationFrame();
35014
- }
35015
- /**
35016
- * Run change detection after the first of setTimeout and requestAnimationFrame resolves.
35017
- *
35018
- * - `requestAnimationFrame` ensures that change detection runs ahead of a browser repaint.
35019
- * This ensures that the create and update passes of a change detection always happen
35020
- * in the same frame.
35021
- * - When the browser is resource-starved, `rAF` can execute _before_ a `setTimeout` because
35022
- * rendering is a very high priority process. This means that `setTimeout` cannot guarantee
35023
- * same-frame create and update pass, when `setTimeout` is used to schedule the update phase.
35024
- * - While `rAF` gives us the desirable same-frame updates, it has two limitations that
35025
- * prevent it from being used alone. First, it does not run in background tabs, which would
35026
- * prevent Angular from initializing an application when opened in a new tab (for example).
35027
- * Second, repeated calls to requestAnimationFrame will execute at the refresh rate of the
35028
- * hardware (~16ms for a 60Hz display). This would cause significant slowdown of tests that
35029
- * are written with several updates and asserts in the form of "update; await stable; assert;".
35030
- * - Both `setTimeout` and `rAF` are able to "coalesce" several events from a single user
35031
- * interaction into a single change detection. Importantly, this reduces view tree traversals when
35032
- * compared to an alternative timing mechanism like `queueMicrotask`, where change detection would
35033
- * then be interleaves between each event.
35034
- *
35035
- * By running change detection after the first of `setTimeout` and `rAF` to execute, we get the
35036
- * best of both worlds.
35037
- */
35038
- async raceTimeoutAndRequestAnimationFrame() {
35039
- const timeout = new Promise(resolve => setTimeout(resolve));
35040
- const rAF = typeof _global['requestAnimationFrame'] === 'function' ?
35041
- new Promise(resolve => requestAnimationFrame(() => resolve())) :
35042
- null;
35043
- await Promise.race([timeout, rAF]);
35044
- this.tick();
35062
+ this.schedule(() => {
35063
+ this.tick();
35064
+ });
35045
35065
  }
35046
35066
  tick() {
35047
35067
  try {