How to know when an Angular.js digest cycle is starting (or finishing)?

angularjs

Solution

OK, I think I finally figured something out, so thought I'd answer my own question.

There's a private Angular method called `$$postDigest` that will callback a function after the end of the next digest cycle. However, it only runs once, and if you try to get it to reschedule itself recursively it'll go into an infinite loop.

To work around this, you can instead use `$$postDigest` in conjunction with a regular `$watch`. If you have a function called `fn` that you want a controller to invoke after every digest cycle, you'd add something like this to the controller function:

...
var hasRegistered = false;
$scope.$watch(function() {
  if (hasRegistered) return;
  hasRegistered = true
  // Note that we're using a private Angular method here (for now)
  $scope.$$postDigest(function() {
    hasRegistered = false;
    fn();
  });
});
...

This is based on a code fragment by Karl Seamon. Obviously it's not ideal to be calling a private method on `$scope`. Hopefully a `$postDigestWatch` method along these lines may eventually get added to Angular.

There's been some confusion in the comments on my original question regarding why you'd want to do this sort of thing: if you're still unclear, check out this blog post I wrote that covers my particular use-case.

Problem

I have an Angular.js app that performs a large set of accounting-type calculations (revenue, costs, profit, etc) across a complex object model. A change to any part of the object model requires all of the calculations to be re-executed. Furthermore, many of the changes to the object model can be made directly from my templates using bindings - there's no need for a controller to intermediate. Many of the calculation results need to both be displayed to the user and contribute to downstream calculations. For example, revenue is displayed to the user but is also used to calculate the profit (which is also displayed to the user). However, there's no point in calculating the revenue twice (it's a complex calculation) on each digest cycle, so an obvious optimization is to memoize when it is first calculated and then reuse this memoized value for the duration of the cycle. The problem is that I need to clear these memoizations either at the beginning or end of the digest cycle. One approach would be for the controller to intercept every possible source of changes and manually unmemoize the object model before proceeding with the calculations. Instead, I'd much prefer to just keep using the bindings that have been set up. As far as I can tell, what I need is a way to know when either the digest cycle is starting, or when it has finished. When I get this notification, I could reset the memoizations. The Angular $watch documentation says: If you want to be notified whenever $digest is called, you can register a watchExpression function with no listener. (Since watchExpression can execute multiple times per $digest cycle when a change is detected, be prepared for multiple calls to your listener.) This is no use to me because clearing the memoizations on each iteration of the digest cycle would defeat the purpose of having them in the first place. I need only one notification per digest cycle. How can I do this, or is there an alternate approach to solving the problem?

Original source

Related problems