49 Commits

Author SHA1 Message Date
Andrew Scott 76e7aa8add refactor(core): Update coalescing to just use patched timers in root zone (#55366)
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.

PR Close #55366
2024-05-02 14:43:10 -07:00
Joey Perrott 31fdf0fbea refactor: migrate core to prettier formatting (#55488)
Migrate formatting to prettier for core from clang-format

PR Close #55488
2024-04-29 09:49:19 -07:00
Andrew Scott 74333e6221 refactor(core): conditionally read data from apply args (#55465)
This commit fixes an error in a previous commit which attempted to read
`data` from the first item in the apply args array, even if it was not
defined.

PR Close #55465
2024-04-22 09:49:05 -07:00
Andrew Scott b47ac4d7c7 refactor(core): Account for Promise.resolve(() => updateState()) pattern in zoneless (#55231)
There is an existing pattern that zone-based applications use to avoid
`ExpressionChangedAfterItHasBeenCheckedError` which is also used in
`NgModel`: update the state in a microtask instead of doing it
synchronously. This defers the state update to another round of change
detection, and with ZoneJS, also still executes in the same event loop.
These types of changes across multiple rounds of change detection were
still executed before the browser paint.

This update allows the `NgModel` workaround to continue working in
Zoneless. This may not be permanent and is certainly still not
advisable. Render hooks (`afterNextRender`/`afterRender`) will execute between these
rounds of change detection when they are intended to be run after all
DOM updates completed.

PR Close #55231
2024-04-19 06:25:46 -07:00
Andrew Scott 18a43b56e5 refactor(core): Prevent running change detection twice with run coalescing and zoneless (#55352)
This commit ensures we correctly handle the exit from the zone.run in
the zoneless scheduler. Run coalescing would delay the `onMicrotaskEmpty` event
until after we have exited the change detection triggered by the
zoneless scheduler and mean that the subscription cannot determine if
`ApplicationRef.tick` should be skipped.

PR Close #55352
2024-04-17 15:50:59 -07:00
Andrew Scott 840c375255 fix(core): do not save point-in-time setTimeout and rAF references (#55124)
Grabbing and saving the references to global `setTimeout` and `rAF`
implementations at a certain point in time can be problematic, espcially
in tests. Tests might call something like `jasmine.clock().install();`
and `jasmine.clock().uninstall();`. If the install happens before we
grab the implementation and then the uninstall happens after, our
scheduling function will be broken because it would have saved a reference to
the jasmine `setTimeout` implementation, which would have since been
cleaned up and will throw  an error when attempting to access `delayedFunctionScheduler`.

There are other scenarios that may apply, not even just for tests, when
patches are applied and removed to the globals.

PR Close #55124
2024-04-01 11:00:11 -07:00
Andrew Scott 0dd441d08f refactor(core): Remove cancelation funtion from scheduler return value (#54578)
Canceling a scheduled callback is not used.

PR Close #54578
2024-03-13 11:09:56 -07:00
Andrew Scott 10c5cdb49c fix(core): ensure change detection runs in a reasonable timeframe with zone coalescing (#54578)
Zone event and run coalescing now races `requestAnimationFrame` and
`setTimeout`. There are tradeoffs to both functions and racing the two
gives us the benefits of both. This is explained in more detail in the
jsdoc comment. This change also aligns the timing of zone coalescing
with what will be used for zoneless.

BREAKING CHANGE: The exact timing of change detection execution when
using event or run coalescing with `NgZone` is now the first of either
`setTimeout` or `requestAnimationFrame`. Code which relies on this
timing (usually by accident) will need to be adjusted. If a callback
needs to execute after change detection, we recommend `afterNextRender`
instead of something like `setTimeout`.

fixes #54544
fixes #44314
fixes #39854 (unverified but seems likely)

PR Close #54578
2024-03-13 11:09:55 -07:00
Andrew Scott 12181b9914 refactor(core): Use single source of truth for ApplicationRef.isStable (#53576)
This commit updates the `ApplicationRef.isStable` implementation to use
a single `Observable` to manage the state. This simplifies the mental
model quite a bit and removes the need for rx operators like
`distinctUntilChanged` and `combineLatest`.

PR Close #53576
2023-12-19 12:42:45 -08:00
Andrew Scott c16e9a7cf0 refactor(core): No default for zone stableness token (#53505)
When an application does not use zones, it does not need a default value
for the zone stableness token. This will allow zoneless applications to
tree-shake a lot of rxjs operators out of `ApplicationRef`.

Note that at the moment, `provideZoneChangeDetection` is included in all
applications as well as the `TestBed` environment. It is not currently
possible to remove the zone stable code as a result. This will be
possible only when we make zones an opt-in rather than opt-out.

PR Close #53505
2023-12-19 09:59:12 -08:00
Andrew Scott 0663708a75 refactor(core): Move code out of application_ref (#53371)
The application_ref is somewhat of a dumping ground for code. This commit
moves things around into more appropriately separate files.

PR Close #53371
2023-12-08 14:29:46 -08:00
AleksanderBodurri 04bffaa41c refactor(core): implement __ignore_ng_zone__ flag (#51339)
This flag allows message event listeners to prevent callbacks from executing within the NgZone if they contain a special `__ignore_ng_zone__` flag.

This functionality is built with Angular DevTools in mind, where it prevents an infinite change detection loop in inspecting applications that have message event listeners:

CD -> Inspected app emits componentTreeDirty event to DevTools -> DevTools emits event to get new component Tree from Inspected app -> Inspected app message event listener fires -> CD

PR Close #51339
2023-09-05 18:16:32 +00:00
Antonio Pekeljevic 1ec2aa902d docs: add missing jsdocs for static ngzone methods (#50924)
PR Close #50924
2023-07-11 08:28:38 -07:00
Alan Agius 4550fe42f7 refactor: use queueMicrotask to schedule micro tasks instead of various helpers (#50485)
`queueMicrotask` is an API which is supported by all browser and Node.js versions.

PR Close #50485
2023-06-15 16:38:21 +02:00
Andrew Scott 4e098fa8a7 refactor(core): move Zone providers to a single provider function (#49373)
This commit moves the providers for `NgZone`-based change detection to a
single provider function. This function is currently called by default
in all places where `NgZone` was provided
(`bootstrapApplication`, `bootstrapModule`, and `TestBed`).

When we want to make Angular applications zoneless by default, we
can make a public provider method that has to be used in order to enable
the zone change detection features. When this method is not called,
Angular would use `NoopNgZone` by default and not initialize any
subscriptions to the `NgZone` stability events.

Side note: There are actually two places that `NgZone` is provided for `TestBed`
(providers in `compileTestModule` and `BrowserTestingModule`). This
likely doesn't need to be in both locations.

PR Close #49373
2023-03-14 09:20:53 -07:00
Andrew Scott d423101a71 refactor(core): Move ApplicationRef.isStable implementation to a provider (#49373)
`ApplicationRef.isStable` is entirely powered by the state of `NgZone`.
The implementation should be part of `NgZone` code rather than built in
to `ApplicationRef`. In the future, `ApplicationRef.isStable` should
likely be removed in favor of the API living on the `NgZone`
class/interface instead.

PR Close #49373
2023-03-14 09:20:52 -07:00
JiaLiPassion 86372538ab refactor(zone.js): remove zone-async-tagging from zone.js (#47416)
1. Remove `zone-async-tagging` implementation from zone.js and move the
implementation to `@angular/core`, so `@angular/core` can import this
package more easily for better treeshaking.
2. Add `async tagging zone` implemenation into `@angular/core` package.
So we don't need to get the `AsyncStackTaggingZoneSpec` from `global`
instance, we can import the `class` directly for better treeshaking.
3. Only load this ZoneSpec when `ngDevMode` is `true`.

PR Close #47416
2022-09-23 14:44:38 -07:00
JiaLiPassion 19e6d9ccd3 feat(core): import AsyncStackTaggingZone if available (#46693)
Import `AsyncStackTaggingZoneSpec` if the user imported
`zone.js/plugins/async-stack-tagging` bundle. So the user can
use `console.trace` to output the `async task` information more
clearly.

PR Close #46693
2022-07-11 20:01:50 +00:00
Ramesh Thiruchelvam 7da389a461 refactor(core): make zone error messages tree shakable (#46480)
Make the error messages tree shakable from the production build to reduce the bundle size.

PR Close #46480
2022-06-24 13:11:14 -07:00
Kristiyan Kostadinov 215db7fbe6 fix(core): error if NgZone.isInAngularZone is called with a noop zone (#44800)
When the user opts into the noop `NgZone`, they usually still interact with the static methods on the non-noop class. This change adds a check to handle the case where zone.js hasn't been loaded.

Fixes #44784.

PR Close #44800
2022-01-24 10:44:43 -08:00
JiaLiPassion dc9fd1aaef fix(core): NgZone coaleascing options should trigger onStable correctly (#40540)
fix https://github.com/angular/components/issues/21674

When setting `ngZoneRunCoalescing` to true, `onStable` is not emitted correctly.
the reason is before this commit, the code looks like this:

```
// application code call `ngZone.run()`
ngzone.run(() => {}); // step 1

// inside NgZone, in the OnInvoke hook, NgZone try to delay the checkStable()

function delayChangeDetectionForEvents(zone: NgZonePrivate) {
  if (zone.lastRequestAnimationFrameId !== -1) { // step 9
    return;
  }
  zone.lastRequestAnimationFrameId = zone.nativeRequestAnimationFrame.call(global, () => { // step 2
    if (!zone.fakeTopEventTask) {
      zone.fakeTopEventTask = Zone.root.scheduleEventTask('fakeTopEventTask', () => {
        zone.lastRequestAnimationFrameId = -1; // step 3
        updateMicroTaskStatus(zone); // step 4
        checkStable(zone); // step 6
      }, undefined, () => {}, () => {});
    }
    zone.fakeTopEventTask.invoke();
  });
  updatemicroTaskStatus(zone);
}

function updateMicroTaskStatus(zone: NgZonePrivate, ignoreCheckRAFId = false) {
  if (zone._hasPendingMicrotasks ||
      ((zone.shouldCoalesceEventChangeDetection || zone.shouldCoalesceRunChangeDetection) &&
       zone.lastRequestAnimationFrameId !== -1)) { // step 5
    zone.hasPendingMicrotasks = true;
  } else {
    zone.hasPendingMicrotasks = false;
  }
}

function checkStable(zone: NgZonePrivate) {
  if (zone._nesting == 0 && !zone.hasPendingMicrotasks && !zone.isStable) { // step 7
    try {
      zone._nesting++;
      zone.onMicrotaskEmpty.emit(null);
    ...
}

// application ref subscribe onMicroTaskEmpty
ngzone.onMicroTaskEmpty.subscribe(() => {
  ngzone.run(() => { // step 8
    tick();
  });
});

```

and the process is:
1. step 1: application call ngZone.run()
2. step 2: NgZone delay the checkStable() call in a requestAnimationFrame, and also set
zone.lastRequestAnimationFrameId
3. step 3: Inside the requestAnimationFrame callback, reset zone.lastRequestAnimationFrameId first
4. step 4: update microTask status
5, step 5: if zone.lastRequestAnimationFrameId is -1, that means no microTask pending.
6. step 6: checkStable and trigger onMicrotaskEmpty emitter.
7. step 7: ApplicationRef subscribed onMicrotaskEmpty, so it will call another `ngZone.run()` to process
tick()
8. step 8: And this new `ngZone.run()` will try to check `zone.lastRequestAnimationFrameId` in `step 9`
when trying to delay the checkStable(), and since the zone.lastRequestAnimationFrameId is already reset
to -1 in step 3, so this ngZone.run() will run into step 2 again.
9. and become a infinite loop..., so onStable is never emit

in this commit, there is a new flag `zone.isCheckStableRunning` added to
prevent re-entry when `shouldCoaleascing` flag is enabled.

PR Close #40540
2021-02-22 10:01:31 -08:00
Misko Hevery d754cc1ea5 Revert "fix(core): NgZone coaleascing options should trigger onStable correctly (#40540)"
This reverts commit 22f9e454a4.
2021-01-29 15:09:37 -08:00
JiaLiPassion 22f9e454a4 fix(core): NgZone coaleascing options should trigger onStable correctly (#40540)
fix https://github.com/angular/components/issues/21674

When setting `ngZoneRunCoalescing` to true, `onStable` is not emitted correctly.
The reason is before this commit, the code looks like this

```
// Application code call `ngZone.run()`
ngZone.run(() => {}); // step 1

// Inside NgZone, in the OnInvoke hook, NgZone try to delay the checkStable()

function delayChangeDetectionForEvents(zone: NgZonePrivate) {
  if (zone.lastRequestAnimationFrameId !== -1) { // step 9
    return;
  }
  zone.lastRequestAnimationFrameId = zone.nativeRequestAnimationFrame.call(global, () => { // step 2
    if (!zone.fakeTopEventTask) {
      zone.fakeTopEventTask = Zone.root.scheduleEventTask('fakeTopEventTask', () => {
        zone.lastRequestAnimationFrameId = -1; // step 3
        updateMicroTaskStatus(zone); // step 4
        checkStable(zone); // step 6
      }, undefined, () => {}, () => {});
    }
    zone.fakeTopEventTask.invoke();
  });
  updateMicroTaskStatus(zone);
}

function updateMicroTaskStatus(zone: NgZonePrivate, ignoreCheckRAFId = false) {
  if (zone._hasPendingMicrotasks ||
      ((zone.shouldCoalesceEventChangeDetection || zone.shouldCoalesceRunChangeDetection) &&
       zone.lastRequestAnimationFrameId !== -1)) { // step 5
    zone.hasPendingMicrotasks = true;
  } else {
    zone.hasPendingMicrotasks = false;
  }
}

function checkStable(zone: NgZonePrivate) {
  if (zone._nesting == 0 && !zone.hasPendingMicrotasks && !zone.isStable) { // step 7
    try {
      zone._nesting++;
      zone.onMicrotaskEmpty.emit(null);
    ...
}

// application ref subscribe onMicroTaskEmpty
ngZone.onMicroTaskEmpty.subscribe(() => {
  ngZone.run(() => { // step 8
    tick();
  });
});

```

And the process is:
1. step 1: application call ngZone.run()
2. step 2: NgZone delay the checkStable() call in a requestAnimationFrame, and also set
zone.lastRequestAnimationFrameId
3. step 3: Inside the requestAnimationFrame callback, reset zone.lastRequestAnimationFrameId first
4. step 4: update microTask status
5, step 5: if zone.lastRequestAnimationFrameId is -1, that means no microTask pending.
6. step 6: checkStable and trigger onMicrotaskEmpty emitter.
7. step 7: ApplicationRef subscribed onMicrotaskEmpty, so it will call another `ngZone.run()` to process
tick()
8. step 8: And this new `ngZone.run()` will try to check `zone.lastRequestAnimationFrameId` in `step 9`
when trying to delay the checkStable(), and since the zone.lastRequestAnimationFrameId is already reset
to -1 in step 3, so this ngZone.run() will run into step 2 again.
9. And become a infinite loop..., so onStable is never emit

In this commit, the `zone.lastRequestAnimationFrameId` reset is moved after `checkStable()` call.

PR Close #40540
2021-01-28 15:53:38 -08:00
cexbrayat 066126ae2f fix(core): remove duplicated noop function (#39761)
The codebase currently contains several `noop` functions,
and they can end up in the bundle of an application.
A recent commit 6fbe21941d tipped us off
as it introduced several `noop` occurrences in the golden symbol files.
After investigating with @petebacondarwin,
we decided to remove the duplicated functions.

This probably shaves only a few bytes,
but this commit removes the duplicated functions,
by always using the one in `core/src/utils/noop`.

PR Close #39761
2020-11-19 12:14:12 -08:00
JiaLiPassion 5e92d649f2 feat(core): add shouldCoalesceRunChangeDetection option to coalesce change detections in the same event loop. (#39422)
Close #39348

Now `NgZone` has an option `shouldCoalesceEventChangeDetection` to coalesce
multiple event handler's change detections to one async change detection.

And there are some cases other than `event handler` have the same issues.
In #39348, the case like this.

```
// This code results in one change detection occurring per
// ngZone.run() call. This is entirely feasible, and can be a serious
// performance issue.
for (let i = 0; i < 100; i++) {
  this.ngZone.run(() => {
    // do something
  });
}
```

So such kind of case will trigger multiple change detections.
And now with Ivy, we have a new `markDirty()` API will schedule
a requestAnimationFrame to trigger change detection and also coalesce
the change detections in the same event loop, `markDirty()` API doesn't
only take care `event handler` but also all other cases `sync/macroTask/..`

So this PR add a new option to coalesce change detections for all cases.

test(core): add test case for shouldCoalesceEventChangeDetection option

Add new test cases for current `shouldCoalesceEventChangeDetection` in `ng_zone.spec`, since
currently we only have integration test for this one.

PR Close #39422
2020-11-16 08:58:50 -08:00
JiaLiPassion 8e17dc0278 fix(core): remove deprecated wtfZoneSpec from NgZone (#37864)
Since `WTF` is deprecated and removed in v9 in this PR #33949, so
in this PR, `WtfZoneSpec` is also removed from `NgZone`.

PR Close #37864
2020-11-12 13:55:45 -08:00
JiaLiPassion b142283ba2 fix(core): unify the signature between ngZone and noopZone (#37581)
Now we have two implementations of Zone in Angular, one is NgZone, the other is NoopZone.
They should have the same signatures, includes
1. properties
2. methods

In this PR, unify the signatures of the two implementations, and remove the unnecessary cast.

PR Close #37581
2020-07-28 09:59:49 -07:00
JiaLiPassion 284123c6ba fix(core): should fake a top event task when coalescing events to prevent draining microTaskQueue too early. (#36841)
Close #36839.

This is a known issue of zone.js,

```
(window as any)[(Zone as any).__symbol__('setTimeout')](() => {
  let log = '';
  button.addEventListener('click', () => {
    Zone.current.scheduleMicroTask('test', () => log += 'microtask;');
    log += 'click;';
  });
  button.click();
  expect(log).toEqual('click;microtask;');
  done();
});
```

Since in this case, we use native `setTimeout` which is not a ZoneTask,
so zone.js consider the button click handler as the top Task then drain the
microTaskQueue after the click at once, which is not correct(too early).

This case was an edge case and not reported by the users, until we have the
new option ngZoneEventCoalescing, since the event coalescing will happen
in native requestAnimationFrame, so it will not be a ZoneTask, and zone.js will
consider any Task happen in the change detection stage as the top task, and if
there are any microTasks(such as Promise.then) happen in the process, it may be
drained earlier than it should be, so to prevent this situation, we need to schedule
a fake event task and run the change detection check in this fake event task,
so the Task happen in the change detection stage will not be
considered as top ZoneTask.

PR Close #36841
2020-06-11 18:54:22 -07:00
Joey Perrott d1ea1f4c7f build: update license headers to reference Google LLC (#37205)
Update the license headers throughout the repository to reference Google LLC
rather than Google Inc, for the required license headers.

PR Close #37205
2020-05-26 14:26:58 -04:00
Joey Perrott 698b0288be build: reformat repo to new clang@1.4.0 (#36613)
PR Close #36613
2020-04-14 12:08:36 -07:00
JiaLiPassion 44623a1161 feat: add a flag in bootstrap to enable coalesce event change detection to improve performance (#30533)
PR Close #30533
2019-11-05 18:58:25 +00:00
Matias Niemelä 082aed6e46 revert: feat: add a flag in bootstrap to enable coalesce event change detection to improve performance (#30533) (#33230)
This reverts commit 21c1e14385.

PR Close #33230
2019-10-17 12:50:04 -04:00
JiaLiPassion 21c1e14385 feat: add a flag in bootstrap to enable coalesce event change detection to improve performance (#30533)
PR Close #30533
2019-10-16 14:38:36 -04:00
JiaLiPassion 3a53e2c960 fix(core): ngNoopZone should have the same signature with ngZone (#32068)
Close #32063

PR Close #32068
2019-10-04 11:48:12 -07:00
Paul Gschwendtner 2200884e55 refactor(core): ensure compatibility with typescript strict flag (#30993)
As part of FW-1265, the `@angular/core` package is made compatible
with the TypeScript `--strict` flag. This already unveiled a few bugs,
so the strictness flag seems to help with increasing the overall code health.

Read more about the strict flag [here](https://www.typescriptlang.org/docs/handbook/compiler-options.html)

PR Close #30993
2019-07-18 14:21:25 -07:00
Pete Bacon Darwin 24521f549c docs: convert all @experimental tags to @publicApi tags (#26595)
PR Close #26595
2018-10-19 14:35:52 -07:00
Igor Minar e3064d5432 feat: typescript 2.9 support (#24652)
PR Close #24652
2018-07-03 13:32:06 -07:00
Pete Bacon Darwin e6516b0229 docs: fix invalid headings (#24000)
PR Close #24000
2018-06-13 16:47:40 -07:00
Michael Giambalvo 37fedd001c feat(core): add task tracking to Testability (#16863)
Allow passing an optional timeout to Testability's whenStable(). If
specified, if Angular is not stable before the timeout is hit, the
done callback will be invoked with a list of pending macrotasks.

Also, allows an optional update callback, which will be invoked whenever
the set of pending macrotasks changes. If this callback returns true,
the timeout will be cancelled and the done callback will not be invoked.

If the optional parameters are not passed, whenStable() will work
as it did before, whether or not the task tracking zone spec is
available.

This change also migrates the Testability unit tests off the deprecated
AsyncTestCompleter.

PR Close #16863
2018-03-14 08:48:48 -07:00
Alex Eagle ef534c0cc1 build: upgrade bazel rules to latest (#20768)
Add enough BUILD files to make it possible to
`bazel build packages/core/test`

Also re-format BUILD.bazel files with Buildifier.
Add a CI lint check that they stay formatted.

PR Close #20768
2017-12-07 11:27:50 -08:00
Juri Strumpflohner 6293ca23c3 docs(core): fix broken NgZone code example (#19291)
The current code example was broken as there were a couple of syntax errors. This commit fixes the demo.

PR Close #19291
2017-11-27 12:14:47 -06:00
Fabian Wiles b55c2ba342 refactor(core): remove prolyfill from error message (#20121)
PR Close #20121
2017-11-10 11:51:15 -08:00
Miško Hevery 344a5ca545 feat(core): support for bootstrap with custom zone (#17672)
PR Close #17672
2017-09-21 16:51:48 -07:00
Miško Hevery 6279e50d78 perf(core): use native addEventListener for faster rendering. (#18107)
Angular can make many assumptions about its event handlers. As a result
the bookkeeping for native addEventListener is significantly cheaper
than Zone's addEventLister which can't make such assumptions.

This change bypasses the Zone's addEventListener if present and always
uses the native addEventHandler. As a result registering event listeners
is about 3 times faster.

PR Close #18107
2017-07-25 15:35:44 -05:00
Vikram Subramanian 7ae7573bc8 fix(core): invoke error handler outside of the Angular Zone (#18269)
In Node.JS console.log/error/warn functions actually resuls in a socket
write which in turn is considered by Zone.js as an async task.

This means that if there is any exception during change detection in a platform-server
application the error handler will make the Angular Zone unstable which
in turn will cause change detection to run on next tick and cause an
infinite loop.

It is also better to run the error handler outside of the Angular Zone
in general on all platforms so that an error in the error handler itself doesn't cause an
infinite loop.

Fixes #17073, #7774.

PR Close #18269
2017-07-21 16:35:23 -05:00
Jason Aden b479ed9407 perf(core): refactor NgZone, decrease size by 1.2Kb (#17773)
- Remove getters
- Hide private methods for better property renaming

```
497893 May 31 11:26 core.umd.js
718073 May 31 11:26 core.umd.js.map
217108 May 31 11:26 core.umd.min.js
575092 May 31 11:26 core.umd.min.js.map
```

```
495594 May 31 11:28 core.umd.js
716943 May 31 11:28 core.umd.js.map
215826 May 31 11:28 core.umd.min.js
574401 May 31 11:28 core.umd.min.js.map
```
diff: 1,282
2017-07-01 10:29:56 -07:00
Alex Eagle 5faf520067 build: Introduce Bazel build rules
So far this just compiles the core and common packages.
2017-06-05 11:18:20 -07:00
Igor Minar 71f5b73296 docs: fix links in api docs 2017-05-03 09:22:32 +01:00
Jason Aden 3e51a19983 refactor: move angular source to /packages rather than modules/@angular 2017-03-08 16:29:27 -08:00