Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compare ways of causing digest

There is this article I saw long time ago: https://coderwall.com/p/ngisma
It describes a method that triggers $apply if we are not in an apply or digest phase.

$scope.safeApply = function(fn) {
  var phase = this.$root.$$phase;
  if(phase == '$apply' || phase == '$digest') {
    if(fn && (typeof(fn) === 'function')) {
      fn();
    }
  } else {
    this.$apply(fn);
  }
};

Angular has the $scope.$evalAsync method (taken from 1.2.14):

   $evalAsync: function(expr) {
            // if we are outside of an $digest loop and this is the first time we are scheduling async
            // task also schedule async auto-flush
            if (!$rootScope.$$phase && !$rootScope.$$asyncQueue.length) {
              $browser.defer(function() {
                if ($rootScope.$$asyncQueue.length) {
                  $rootScope.$digest();
                }
              });
            }

            this.$$asyncQueue.push({scope: this, expression: expr});
          }

Which calls digest if we are not in a phase and adds the current invocation to the asyncQueue.

There is also the $apply, $digest and $timeout methods. It is confusing.
What is the difference between all ways mentioned to trigger a digest cycle (a dirty check and data binding)?
What is the use case for each method?
Is safeApply() still safe? :)
What alternative we have instead of that safeApply() (in case we call $apply in the middle of a digest cycle)?

like image 746
Naor Avatar asked Nov 03 '14 07:11

Naor


People also ask

What are 2 different ways food is digested?

Chemical and mechanical digestion are the two methods your body uses to break down foods. Mechanical digestion involves physical movement to make foods smaller. Chemical digestion uses enzymes to break down food.

What are the 3 methods of digestion?

The digestive system carries out three primary processes: mixing food, moving food through the digestive tract (peristalsis) and using chemicals to break down food into smaller molecules.

What are two factors that affect digestion?

Diet composition, exercise, functional disorders like irritable bowel disease (IBS), thyroid dysfunction and metabolic disorders—such as diabetes—to name a few. Food itself also plays a major factor.


1 Answers

As a high level introduction I would say that it is rarely required to actually initiate your own digest cycle, since angular handles most cases.

That being said let's dive into the question.

As a very high level, the $digest loop looks like this:

Do:
- - - If asyncQueue.length, flush asyncQueue.
- - - Trigger all $watch handlers.
- - - Check for "too many" $digest iterations.
While: ( Dirty data || asyncQueue.length )

So basically $evalAsync is adding the function to the asyncQueue and defering a digest if it needs to. However if it's already in a digest cycle it will flush the asyncQueue and it will just call the function.

You may notice that this is very similar to the safeApply. One difference is that instead of adding the function to the asyncQueue it just calls it, which can happen in the middle of a cycle for instance. The other difference is that it exposes and relies on a $$ variable, which are intended to be internal.

The most important difference however between $evalAsync and $apply from $digest (I will get to $timeout below) is that $evalAsync and $apply starts the digest at the $rootScope but you call the $digest on any scope. You will need to evaluate your individual case if you think you need this flexibility.

Using $timeout is very similar to $evalAsync again except that it will always defer it out of the current digest cycle, if there is one.

$timeout(function() {
    console.log( "$timeout 1" );
});
$scope.$evalAsync(function( $scope ) {
    console.log( "$evalAsync" );
});

If you already in a digest cycle will give you

$evalAsync
$timeout 1

Even though they are called in the opposite order, since the timeout one is delayed to the next digest cycle, which it instantiates.

EDIT For the questions in the comment. The biggest difference between $apply and $evalAsync as far as I can tell is that $apply is actually triggering the $digest cycle. To you all this means is that you need to be sure you call $apply when you aren't in a digest cycle. Just for transparency, the following code is also valid:

$scope.$apply(function() {
    $scope.$evalAsync(function() {
    });
});

Which will call the apply function, add the no-op function to the $asyncQueue and begin the digest cycle. The docs say it is recommended to use $apply whenever the model changes, which could happen in your $evalAsync

The difference between fn(); $scope.apply() and $scope.apply(fn) is just that the $scope.apply(fn) does a try catch for the function and explicitly uses $exceptionHandler. Additionally, you could actually attempt to cause digest cycles in fn, which will change how the apply is handled.

I'd also like to point out at this time that it is even more complicated in 1.3 assuming it stays this way. There will be a function called $applyAsync used to defer apply calls (reference).

This post compiled some information from This blog and this so post plus some of my experience.

Hope this helped!

like image 101
hassassin Avatar answered Oct 01 '22 20:10

hassassin