These changes are essentially the same as those introduced in
angular#45273, but they include backward compatibility
for applications that explicitly rely on the order in which microtasks are drained.
This is critically important for our code and other third-party code, which is
beyond our control, to work properly. If a microtask is scheduled within an event
listener to be executed "later", it should indeed be executed later and not synchronously,
as this would break the expected flow of code execution.
The simple code that reproduces the behavior that exists now:
```ts
Zone.current.fork({name: 'child'}).run(() => {
const div = document.createElement('div');
div.style.height = '200px';
div.style.width = '200px';
div.style.backgroundColor = 'red';
document.body.appendChild(div);
function listener() {
Promise.resolve().then(() => {
div.style.height = '400px';
});
}
div.addEventListener('fakeEvent', listener);
div.dispatchEvent(new Event('fakeEvent'));
console.log(div.getBoundingClientRect().height); // 400
});
```
The code above logs 400 as the height, but it should actually log 200 because the
height is updated in a microtask within the event listener.
When using Angular with microfrontend applications, especially when other apps might be
using React, zone.js can disrupt the classical order of operations. For example, when using a
`react-component/trigger`, it schedules a microtask within an event listener using
`Promise.resolve().then(...)` to determine whether the event needs to be re-dispatched.
The event is re-dispatched when the layout has changed, which is why a microtask is used.
With this change, we introduce a global configuration flag,
`__zone_symbol__enable_native_microtask_draining`, to allow consumers to enable
microtask draining within a browser microtask.
This flag is necessary to prevent any breaking changes resulting from this modification.
The previous attempt to address this issue caused a significant number of failures in g3.
Therefore, we are hiding that fix behind the configuration flag.
Closes angular#44446
Closes angular#55590
Closes angular#51328
(cherry picked from commit fc6a7eea68)
When Zone patches Promise, it uses ZoneAwarePromise. The new Promise.try API was undefined on ZoneAwarePromise, making it unavailable when zone was present. This change gracefully passes through Promise.try to the native Promise implementation, if available, without patching it to execute in the right zone (our stance is not to add new patches but avoid destructively making new APIs unavailable).
Fixes#67057
Change direct deps in bazel targets and import specifiers within files to maintain strict deps requirements ahead of enabling strict deps tests in the repo
PR Close#63323
When using `file:` renovate updates fails due to ` ERR_PNPM_LINKED_PKG_DIR_NOT_FOUND ` this is due to a different behaviour between `link:` amd `file:`. `link:` however will not fail when the directory does not exist.
PR Close#63111
During `pnpm install` in a monorepo setup, the `packages/zone.js/test/typings` project would fail with the error: `ERR_PNPM_LINKED_PKG_DIR_NOT_FOUND: Could not install from "/.../node_modules/typescript" as it does not exist.`
PR Close#63105
Benefits of auto-ticking mock clocks have been described in other PRs,
such as https://github.com/jasmine/jasmine/pull/2042 and
https://github.com/sinonjs/fake-timers/pull/509. In short, `fakeAsync`
cannot work when some tasks are required to be truly async, such as XHRs
or observers like ResizeObserver. In addition, auto ticking mock clocks
can be applied to tests without the tests then needing to update
everything to manually flush timers.
PR Close#62135
This ensures that test functions with arguments (e.g. `it.each` in jest)
are forwarded to the test function. This does not apply to jasmine,
which assumes the only arguments needed would be the `done` function.
fixes#61717
PR Close#61755
As an alternative to monkey patching vitest, this change adds a method that could be used
for manually running functions inside a shared proxy zone. If used inocrrectly,
this would mean that
the `fakeAsync` closure may not capture all timers and microtasks if it
invokes things created in a zone that was already forked (e.g. creating
a component in a beforeEach:
2699dd6555/packages/zone.js/lib/jasmine/jasmine.ts (L363-L371))
PR Close#61626
This commit updates a flaky test to increase the amount of work (more `for` loop iterations) to minimize the chance of getting the same timestamp after that work.
PR Close#59653
This commit updates the `fetch` patch for zone.js. Currently, we're attaching an
`abort` event listener on the signal (when it's provided) and never removing it.
We should be good citizens and remove event listeners whenever objects need to be
properly collected. In Firefox, when saving a heap snapshot and running it through
`fxsnapshot`, querying `AbortSignal` will print a so-called "CaptureMap" with a list
of "lambdas," indicating that the signal is not garbage collected because of the event
listener lambda function.
PR Close#57882
From the internal issue on the matter:
> When using the standard Jasmine version of it promises returned by the body function are automatically awaited. The Catalyst version of it is fake-async, so awaiting the promise does not make sense; however it would be nice if Catalyst automatically flushed the promise to replicate the experience of using standard it. This would allow users to do the following:
```
it('should fail later', async () => {
await new Promise(r => setTimeout(r));
fail('failure');
});
```
> In Catalyst today the above test will pass. If this proposal to automatically flush the resulting promise were implemented it would fail.
Flushing after the tests complete has been the default behavior inside
Google since 2020. Very few tests remain that use the old behavior of
only flushing microtasks. The example above would actually fail with
`fakeAsync` due to the pending timer, but the argument still remains the
same. We might as well just flush if we're going to fail the test
anyways by throwing if there's no flush at the end.
BREAKING CHANGE: `fakeAsync` will now flush pending timers at the end of
the given function by default. To opt-out of this, you can use `{flush:
false}` in options parameter of `fakeAsync`
PR Close#57240
The `Timeout` object in Node.js has a `refresh` method, used to restart `setTimeout`/`setInterval` timers. Before this commit, `Timeout.refresh` was not handled, leading to memory leaks when using `fetch` in Node.js. This issue arose because `undici` (the Node.js fetch implementation) uses a refreshed `setTimeout` for cleanup operations.
For reference, see: 1dff4fd9b1/lib/util/timers.js (L45)Fixes: #56586
PR Close#56852
Prior to this commit, a memory leak occurred when the `abort` listener was
not removed from the `AbortSignal`. We introduced a fix to remove the event
listener, but it was erroneously stored on the `taskData`, which is a shared
global object. Consequently, when something attempted to remove an event listener,
it immediately removed the last stored abort listener. As a result, events would
never be canceled.
We have now rectified this by storing the remove abort listener function directly on
the task itself. This adjustment ensures that the abort listener is tied only to the
specific task. When the `abort` function is called, it cancels the task. Therefore, it
is safe to associate the cleanup function directly with the task.
Closes: #56148
PR Close#56160