-
Notifications
You must be signed in to change notification settings - Fork 27.4k
fix($rootScope): don't allow explicit digest calls to affect $evalAsync #15494
Conversation
@gkalpak So after thinking this over for a bit, I don't think having internal This fix does address the case of either |
I personally am fine with such a change (as I am not a strong believer in the local
Yet, imo voiding an optimization is preferrable to unexpectedly broken behavior (because some bindings did not propagate). I am a little on the fence on whether this is a breaking change though, as it does break certain assumptions that used to hold. (Although it is not very likely to actually breaks apps, I wonder how tests could be affected by this.) tl;dr Looks OK for 1.7.x - Still not sure about 1.6.x |
I am not that keen on this change. |
The problem with the current implementation is that a global // Imagine two totally unrelated scopes (e.g. two directives scopes coming from
// third party libraries and located in distinct and unrelated parts of the page.
var scope1 = $rootScope.$new();
var scope2 = $rootScope.$new();
// `scope1` calls `$evalAsync()` (and expects the updated values to propagate to the view
scope1.$evalAsync(someExpr);
// Before the next tick (when `$rootScope.$digest()` will be called)
// `scope2` calls `$digest()` to update some value in its own view
scope2.$digest();
// Now, because of that last call, `someExpr` will be evaluated, the `asyncQueue` will be
// emptied and as a result, `$rootScope.$digest()` won't run. |
Yes, and I am saying that people shouldn't be doing So how about we do not drain the async queue if the call to |
@petebacondarwin That would be a lot worse than the current behavior. You would no longer be able to use anything that relies on Even worse, |
…st()` Previously, if an async task was scheduled (via `$evalAsync()`) on a scope and `$digest()` happened to be called on another, unrelated scope (local digest), then the `asyncQueue` would be drained but `$rootScope.$digest()` would not be called (potentially preventing the changes to propagate correctly through the app). This commit fixes it by keeping track on whether `$digest()` has been called on `$rootScope` or not and call `$rootScope.$digest()` if necessary (even if the `asyncQueue` has been drained by a local `$digest()`). Fixes angular#15127 Closes angular#15494
How about #15501? It should fix the issue with minimal impact on the current behavior (and the optimization). |
That's definitely an alternative, but I'm unclear on why that's better since it causes additional loops of the digest cycle whereas this keeps it as minimal as possible. Is it to address situations where someone is mutating global (or just outside state) inside a digest cycle that was initated with |
I am not sure it is better. It is just an alternative, I guess 😃 Initially I thought it was a good idea, because this keeps the execution order closer to what I would expect and it might also be less expensive if the local digest doesn't stabilize immediately. But today it doesn't seem any better than your version (both having pros and cons). |
After thinking it some more, I think this one is better. So, I am again back to: Looks OK for 1.7.x - Still not sure about 1.6.x |
What kind of change does this PR introduce? (Bug fix, feature, docs update, ...)
fix
What is the current behavior? (You can also link to an open issue here)
#15127
What is the new behavior (if this is a feature change)?
If an
$evalAsync
call occurs prior to a$digest
being invoked on a scope other than the$rootScope
, the target of the operation is changed to the$rootScope
in order to allow the watchers that may have reacted to the$evalAsync
operation to occurDoes this PR introduce a breaking change?
Nope
Please check if the PR fulfills these requirements
Other information:
Fixes #15127