Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(upgrade): call setInterval outside the Angular zone #16836

Merged
merged 1 commit into from
Jun 6, 2017

Conversation

heathkit
Copy link
Contributor

@heathkit heathkit commented May 17, 2017

This wraps the $interval service when using upgrade to run the
$interval() call outside the Angular zone. However, the callback is
invoked within the Angular zone, so changes still propagate to
downgraded components. Fixes #16349.

Please check if the PR fulfills these requirements

What kind of change does this PR introduce? (check one with "x")

[ X] Bugfix
[ ] Feature
[ ] Code style update (formatting, local variables)
[ ] Refactoring (no functional changes, no api changes)
[ ] Build related changes
[ ] CI related changes
[ ] Other... Please describe:

What is the current behavior? (You can also link to an open issue here)

In AngularJS, the $interval service wraps setTimeout so that $digest will be called after the interval fires. Because $interval does not block stability in AngularJS, we recommended that Protractor users use it instead of $timeout for long running or polling tasks.

Upon upgrading, those user's tests start failing, because the $interval service will block stability for the Angular zone, whereas it didn't in AngularJS.

What is the new behavior?

This wraps the $interval service so that setTimeout is called outside the Angular zone. However, the callback is still run within the Angular zone, so that changes still propagate (ie, this does not address #6385 at all).

Does this PR introduce a breaking change? (check one with "x")

[ ] Yes
[ ] No
[X] I don't know?

I think it technically might be a breaking change, but the previous behavior was not what people would expect when upgrading from AngularJS.

const subscription =
this.ngZone.onMicrotaskEmpty.subscribe(() => $rootScope.$digest());
const subscription = this.ngZone.onMicrotaskEmpty.subscribe(() => {
if (!$rootScope.$$phase) {
Copy link
Member

Choose a reason for hiding this comment

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

This is something we've been trying to avoid 😃
It can hide actual problems in user code. It is better to identify the cause of this and stop it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In this case, the cause is that $interval runs the callback with evalAsync then triggers $rootScope.$apply. If we call ngZone.run() within this callback, it looks like that can synchronously trigger onMicrotaskEmpty, which also calls $rootScope.$digest.

Using $$phase is ugly here, though, I agree. The alternative is to put ngZone run in a setTimeout() so it'll run on the next vm turn (and thus, outside of the digest started by $interval). Either way is fine with me, or if there's a third option I haven't considered, let me know :)


tick(200);
expect(intervalDone).toEqual(true);
$interval.cancel(id);
Copy link
Member

Choose a reason for hiding this comment

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

I would verify that the returned value (id) is as expected and that .cancel() works (just to be sure).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The fakeAsync zone will throw an error if the test ends while there are pending tasks, but I put in some expectations around cancelling.

@heathkit heathkit force-pushed the interval-fix branch 3 times, most recently from 45004db to 55d2df1 Compare May 18, 2017 19:45
@heathkit heathkit changed the title fix(upgrade): Call setInterval outside the Angular zone. fix(upgrade): call setInterval outside the Angular zone May 18, 2017
(intervalDelegate: angular.IIntervalService) => {
// Wrap the $interval service so that setInterval is called outside NgZone,
// but the callback is still invoked within it. This is so that $interval
// won't block stability, which was the case in AngularJS.
Copy link
Member

Choose a reason for hiding this comment

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

Suggestion: "which preserves the behavior from AngularJS"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

@mhevery mhevery added the action: merge The PR is ready for merge by the caretaker label Jun 6, 2017
This wraps the $interval service when using upgrade to run the
$interval() call outside the Angular zone. However, the callback is
invoked within the Angular zone, so changes still propagate to
downgraded components.
@angular-automatic-lock-bot
Copy link

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 Sep 11, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
action: merge The PR is ready for merge by the caretaker cla: yes hotlist: google
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Testability API not stabalizing - ng-upgrade
7 participants