Compare ways to create a digest

There is this article that I saw a long time ago: https://coderwall.com/p/ngisma
It describes a method that runs $ apply if we are not in the application 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 a $scope.$evalAsync (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 call is digested if we are not in phase and add the current call to asyncQueue.

There is also a $ apply, $ digest and $ timeout method. This is confusing. What is the difference between all the mentioned methods for starting a digest cycle (dirty validation and data binding)?
What is the use case for each method?
Is it safe safe () safe? :)
What alternative do we have, not safeApply () (if we call $ apply in the middle of the digest loop)?

+6
source share
2 answers

As a high-level introduction, I would say that you rarely need to initiate your own digest cycle, since angular handles most cases.

This suggests plunging 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 adds a function to asyncQueue and asyncQueue digest if necessary. However, if it is already in the digest loop, it will clear asyncQueue , and it will just call the function.

You may notice that this is very similar to safeApply . One difference is that instead of adding a function to asyncQueue it simply calls it, which can happen, for example, in the middle of a loop. Another difference is that it provides and relies on the $$ variable, which must be internal.

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

Using $timeout again is very similar to $evalAsync , except that it will always defer it from the current digest cycle, if any.

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

If you are already in the digest cycle give you

 $evalAsync $timeout 1 

Even though they are called in the reverse order, since the timeout is delayed until the next cycle, the digest that it creates.

EDIT . Questions in the comment. The biggest difference between $apply and $evalAsync , as far as I can tell, is that $apply actually calls the $digest loop. All this means to you that you need to be sure that you call $apply when you are not in the digest loop. For transparency, the following code is also valid:

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

What will call the apply function, add the no-op function to $asyncQueue and start the digest cycle. The docs say it is recommended to use $apply whenever a model changes that might happen in your $evalAsync

The difference between fn(); $scope.apply() fn(); $scope.apply() and $scope.apply(fn) is that $scope.apply(fn) makes a catch attempt for a function and explicitly uses $exceptionHandler . In addition, you can really try to call digest cycles in fn , which will change the way you process your application.

I would also like to point out at this time that it is even more complex in 1.3, assuming that it remains so. There will be a function called $applyAsync , used to defer calls ( link ).

This post has gathered some information from this blog and this one so post plus some of my experiences.

Hope this helps!

+2
source

There is only one way to start a digest cycle: Call $digest . It is rarely called directly.

$apply is essentially a safe wrapper for $digest . Safe means that it handles errors. Therefore, most of the time, this is the method you want to call to propagate the changes. Another important difference is that it always starts a digest cycle in the root area, while $digest can be called in any scope and affects only this area and its descendants.

Many services and directives start a digest cycle, usually by calling $apply . The only thing you need to know is that you need to call $apply yourself or it is already called by a service / directive.

Is safeApply still safe?

Yes, because there is another way to start a digest cycle.

What is our alternative safeApply () instead (if we call $ apply in the middle of the digest loop)?

Do not call $apply in the middle of a digest loop. Jokes aside. Being in this situation is almost always a design problem. It’s better to solve this problem than to cover it.

In any case, the method used by the solution to check the $$phase is the only way to find out if the digest cycle is working. As you have shown, it is even used internally by Angular. Enough to do if (!$rootScope.$$phase) . But keep in mind that this is a hack that may break with the new version of Angular. BTW: The monkey correcting the upper region, as suggested by the author, will make the solution useless for isolated regions.

Angular's way to bind data can really be hard to understand for someone new to Angular. But basically it comes down to the following: for every change that needs to be recognized / processed, someone should call $apply (or $digest , for that matter). It. This is supposed to be done through directives and services. It's actually quite difficult to get the "$ apply is is progress" error if you did everything right.

+1
source

Source: https://habr.com/ru/post/977638/


All Articles