Skip to content

fix(zone.js): always run microtasks in Promise.then to avoid drain microtasks too early.#45273

Closed
JiaLiPassion wants to merge 3 commits into
angular:mainfrom
JiaLiPassion:drain-microtask
Closed

fix(zone.js): always run microtasks in Promise.then to avoid drain microtasks too early.#45273
JiaLiPassion wants to merge 3 commits into
angular:mainfrom
JiaLiPassion:drain-microtask

Conversation

@JiaLiPassion
Copy link
Copy Markdown
Contributor

@JiaLiPassion JiaLiPassion commented Mar 5, 2022

always drainMicrotaskQueue in Promise.then.

Consider the following case.

const listener = () => {
  Promise.resolve().then(() => console.log(`promise then`));
};

document.body.addEventListener('click', listener);
document.body.click();
console.log('main stack');

Without zone.js patch, the output should be

main stack
promise then

since Promise.resolve().then() should run as microtask after main stack finish.
But with zone.js patch, it becomes

promise then
main stack

The reason is currently zone.js maintain a microtask queue themselves, and after a task is invoked (in this case, the click eventTask), it will try to drainMicrotaskQueue synchronizely if the task is the top task.

This PR make sure that the dranMicroTaskQueue happen inside a Browser native microTask.

This new behavior is the correct behavior but it is very risky since it changes the microTask consuming timing and may impact a lot of existing apps.

Close #44446, #41506

@JiaLiPassion JiaLiPassion changed the title just for test (WIP): always run microtasks in Promise.then to avoid drain microtasks too early. Mar 5, 2022
@JiaLiPassion JiaLiPassion requested a review from alxhub March 5, 2022 02:10
@JiaLiPassion JiaLiPassion added risk: high risky Identifies a PR that has a level of risk to merging labels Mar 5, 2022
@JiaLiPassion JiaLiPassion changed the title (WIP): always run microtasks in Promise.then to avoid drain microtasks too early. (fix): always run microtasks in Promise.then to avoid drain microtasks too early. Mar 5, 2022
@JiaLiPassion JiaLiPassion added the area: zones Issues related to zone.js label Mar 5, 2022
@ngbot ngbot Bot added this to the Backlog milestone Mar 5, 2022
@JiaLiPassion JiaLiPassion changed the title (fix): always run microtasks in Promise.then to avoid drain microtasks too early. fix(zone.js): always run microtasks in Promise.then to avoid drain microtasks too early. Mar 5, 2022
@JiaLiPassion JiaLiPassion force-pushed the drain-microtask branch 3 times, most recently from 288665d to 1217dda Compare March 6, 2022 05:29
Copy link
Copy Markdown
Member

@devversion devversion left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems reasonable to me, but definitely also breaky (in terms of apps relying on this synchronous draining currently). LGTM

@AndrewKushnir
Copy link
Copy Markdown
Contributor

AndrewKushnir commented Mar 15, 2022

Presubmit + Global Presubmit.

@AndrewKushnir
Copy link
Copy Markdown
Contributor

Quick update after running a global presubmit: this PR breaks a number of targets (40), which would require additional investigation. Adding the "blocked" label for now.

…crotasks too early

Always `drainMicrotaskQueue` in `Promise.then`.

Consider the following case.

```
const listener = () => {
  Promise.resolve().then(() => console.log(`promise then`));
};

document.body.addEventListener('click', listener);
document.body.click();
console.log('main stack');
```

Without `zone.js` patch, the output should be
```
main stack
promise then
```
since `Promise.resolve().then()` should run as microtask after `main stack` finish.
But with `zone.js` patch, it becomes
```
promise then
main stack
```

The reason is currently `zone.js` maintain a `microtask queue` themselves, and after a `task` is invoked (in this case, the `click` eventTask), it will try to `drainMicrotaskQueue` synchronizely if the `task` is the `top task`.

This PR make sure that the `dranMicroTaskQueue` happen inside a Browser native microTask.

This new behavior is the correct behavior but it is very risky since it changes the microTask consuming timing and may impact a lot of existing apps.

Close angular#44446, angular#41506
In jasmine `onComplete()` callback, we used to schedule a new microTask
to ensure there is a top task is running to avoid the early draining
microtask behavior, since now zone.js always use a native Promise.then
to drain the microtask queue, we don't need to do this any longer.
In the mocha runTest patch, we used to schedule a microtask to avoid
drain microtask queue to early since we have a maunal top task, since
zone now drain microtask queue in a native microtask Promise.then,
we don't need to do this manual schedue any longer.
@angular-robot angular-robot Bot requested a review from devversion April 1, 2024 15:21
@angular-automatic-lock-bot
Copy link
Copy Markdown

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot Bot locked and limited conversation to collaborators Jul 7, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area: zones Issues related to zone.js risk: high risky Identifies a PR that has a level of risk to merging state: blocked

Projects

None yet

Development

Successfully merging this pull request may close these issues.

zone.js drains microtasks too early in synchronous event call

5 participants