angular: UpgradeModule $rootScope.$digest error even though we are not triggering a digest manually.

I’m submitting a…


[ ] Regression (a behavior that used to work and stopped working in a new release)
[x] Bug report  
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question

Current behavior

This issue relates to this stackOverflow issue and this conversation and this conversation in gitter

We authorize a user with Google SignIn, and then use ui-router to send them to the landing page, doing this causes a $rootScope digest already in process error.

We do not trigger a $digest / $apply explicitly. We don’t route a user with $location, location, or anything. We use ui-router, but it’s not ui-router that triggers the $digest within the $digest. As far as I can tell, it appears to be this line in NgUpgrade.

Expected behavior

No $rootScope.$digest error

Minimal reproduction of the problem with instructions

If you do not see a digest error in console, repeat the above process up to 5 times and you will eventually see the error. Here is a short snippet of the code:

https://pastebin.com/1h7AvuJt

What is the motivation / use case for changing the behavior?

If it’s a bug it’s a bug.

Environment


Angular version: X.Y.Z

4.2.4

Browser:
- [x] Chrome (desktop) version XX
- [ ] Chrome (Android) version XX
- [ ] Chrome (iOS) version XX
- [ ] Firefox version XX
- [ ] Safari (desktop) version XX
- [ ] Safari (iOS) version XX
- [ ] IE version XX
- [ ] Edge version XX
 
For Tooling issues:
- Node version: XX  
- Platform:  

Others:

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Comments: 20 (11 by maintainers)

Most upvoted comments

@andrewl-telnyx: BTW, something I noticed while looking at the code is that the DSCache#setRecycleFreq() is using setInterval() to schedule an operation once per second for each cache (I saw 3 of them iirc). Since setInterval is called from inside AngularJS, which is bootstraped inside the Angular zone, it means that 3 times per second you get a new onMicrotaskEmpty emission, i.e. 3x Angular change detection and 3x $digest per second (for no change). I took a quick look, so take it with a grain of salt. But worth looking into.

(This is probably not related to this issue - just mentioning it as a heads-up.)

You might also be interested in a new API that will be introduced in 5.x, which will allow you to bootstrap AngularJS outside the Angular zone and avoid the $digest on each onMicrotaskEmpty emission. The docs are still in progress, but you can get a preview here.

It is more suited for architectures that try to keep the two parts decoupled, so not sure if it works for you. Not that interleaving Angular/AngularJS components won’t work, but you might have to manually trigger change detections too often (e.g. if you rely on “global” app state through services, instead of propagating it via component inputs).

@andrewl-telnyx , @petebacondarwin, I would like to help to debug about the Zone issue, I will try to debug why zone not trigger onMicroTaskEmpty

Glad you have a workaround. If you find that you start missing change detections then you’ll know where to look.

The reason your solution works is that you are no longer running the handleAuth2Result function inside the “Angular” zone, which means that when the popstate event is triggered our ngUpgrade onMicroTaskEmpty handler is not called, which in turn does not trigger the digest.

Normally this is a dangerous thing to do, because anything that follows in that zone (all async tasks that are triggered via that original call to handleAuth2Result) will not trigger change detection between the AngularJS and Angular parts of the app.

But in this case I imagine that it is not going to impact your app too much. I guess it depends on what is in that handler.

So… the underlying process looks like this:

  1. the url is changed from outside $location (maybe the Google Auth library or maybe ui-router - the latter I think)
  2. On the next digest, this change to the URL triggers $location’s watcher, which tries to resynchronise itself with the browser URL
  3. in doing so it writes to window.location.hash
  4. this triggers a PopState event
  5. The Angular PlatformLocation is being used, which sets up a handler for PopState events.
  6. the Zone library wraps this handler as a ZoneAwareCallback
  7. For some reason, and only when the logging in for the first time, after the cache has been cleared, the Zone library decides that there is a need to run the MicroTaskEmpty hook
  8. This hook is handled by ngUpgrade, which triggers a new digest and BAM there is the error

The question I am having trouble answering is what is it about this first login that causes the Zone library to trigger the hook.

I think we might need to pull in the big guns (@mhevery perhaps) to help dig through what is happening in Zones.

@petebacondarwin we turned off bugsnag and commented it out in code, and the problem still occurs. we’re going to get you a non minified version here in a bit.