postDigestQueue poor performances #14534
Description
One of our page is very heavy. To decrease our watcher count and to accelerate the angular digest cycle we are using a lot the On-Time-Binding syntax ::
. We are also using angular-bind-notifier to avoid unnecessary watchs over our expressions on this page.
This strategy allowed us to reduce the angular digest cycle considerably.
But this strategy has one down side: It uses the $$postDigest
(postDigestQueue
) to unwatch
the expression after it has been evaluated successfully.
So what ?
At the end of the digest, angular will run through the postDigestQueue
. As we used a lot of On-Time-Binding expressions, our postDigestQueue
can grow up to more than 100 000 queued tasks.
The problem is that angular uses the following code to loop over the queue:
while (postDigestQueue.length) {
try {
postDigestQueue.shift()();
} catch (e) {
$exceptionHandler(e);
}
}
The shift method removes the element at the zeroeth index and shifts
the values at consecutive indexes down, then returns the removed
value.https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift
Yes, Array.prototype.shift()
is a very expensive call when there is a lot of elements in the array.
Our digest cycle will sometimes take more than 20 seconds because of that.
When we change the previous code with the following one, it is way faster:
for (var i = 0; i < postDigestQueue.length; i++) {
try {
postDigestQueue[i]();
} catch (e) {
$exceptionHandler(e);
}
}
postDigestQueue.length = 0;
Is there a reason why they did that ?
Shouldn't we use that much the one time binding ?
I could see one reason: if a task add itself a need task in the queue. Is is possible ? ($$postDigest
is a private queue) An answer would be to use pop
instead of shift
if the order of execution is not important, but is it ?
Edit: The order seems important because the postDigestQueue
is used with the animations.