-
Notifications
You must be signed in to change notification settings - Fork 27.4k
Manually calling $digest on a scope prevents $rootScope.$digest from executing for pending $evalAsyncs #15127
Comments
This test case is a tad bit simpler http://plnkr.co/edit/Clqp1Y It doesn't involve $q and calls $scope.$evalAsync directly. |
Was thinking about this earlier since digesting just a single branch can be a sweet performance optimization, but not having the the Gist of the problem is that |
I don't think that is a problem -unless I misunderstood what you mean. If you Having separate async queues would be ideal, if we could still guarantee the correct order of execution (which would get too complicated - I think). (Disclaimer: Haven't thought this through yet.) |
If you control the entire subtree, then you can definitely maintain unidirectional dataflow to make this work. The problem is that a blanket statement like this would break composability/isolation for components; you couldn't safely call |
For reference, each scope did used to have its own execution queue, but it was changed way back in 1.1.1 Reverting to that model would break this optimization, or at least make it more difficult to accomplish as we need to know whether or not child scopes have pending tasks to execute either through visiting the scope, or via a counter that gets incremented/decremented. https://github.com/angular/angular.js/blob/master/src/ng/rootScope.js#L824-L828 |
Interesting! So, going back to multiple queues is not worth it, since it would break other usecases as mentioned in the commit message of 331cd5a. It would be nice to fix OP's usecase though. (No idea what it could be atm.) |
Isn't the workaround to use $applyAsync instead of $evalAsync? Don't $applyAsync and $evalAsync use separate queues, and the local child scope $digest call will only empty every $evalAsync queue? It seems to work. I only changed $evalAsync to $applyAsync: http://plnkr.co/edit/9deFSvmLs2tJEtgsfNkr?p=preview |
Yes, this would work, but it doesn't help in this usecase where something is using Back to the problem at hand, a simpler solution might be calling |
@samal84 Could you please elaborate on what you are suggesting? If you are implying that one could simply forego $q/$evalAsync in their code in order to avoid this issue then that is not really feasible. In the repro app |
@gkalpak Call |
@BrandonLWhite @gkalpak Yes my workaround only works when you have control over where $evalAsync/$applyAsync gets called. |
if (asyncQueue.length) {
$rootScope.$digest();
} We could remove the |
So while that would work as an easy fix, you end up running potentially needless digest cycles. While that wouldn't break anyone, running a full This also breaks the optimization since you'll no longer be able to call |
I would rather break the optimization than have broken behavior. But I agree that potentially running an extra digest (even if it will only affect some cases) isn't ideal. We should come up with a more appropriate solution. |
…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
Issue
This is a bug report for an intermittent issue we are seeing where due to specific timing of asynchronous callbacks executing causes $rootScope.$digest from being called when it is expected that Angular should otherwise do so.
I have a plunkr that simulates the scenario to induce the failure every time.
The current behavior is that a controller "A" scope is not digested upon conclusion of a $.then when another controller "B" scope's $digest is called manually.
This plunkr demonstrates the issue. Please follow these steps:
The expected behavior is that ctrl.result ends displaying "Finished" and ctrl.shared.foo matches what is shown for ctrl2.shared.foo.
What is being demonstrated is the $digest for ctrl2 causes the digest for ctrl to be skipped. Because ctrl is using $q the expectation is that its scope will be digested at the conclusion of the $q.then function (presumably as a result of a $rootScope.$digest being performed by Angular).
This workaround (undesirably) inspects
It appears that all browsers and all "recent" versions of Angular exhibit the issue.
What is going on?
In our application, here is what's going on:
Tracing through Angular here is what I've come up with:
It seems like scope.$digest shouldn't be processing the asyncQueue like this unless the scope being digested is the rootScope.
The text was updated successfully, but these errors were encountered: