javascript 解决 $rootScope:infdig 无限 $digest 循环

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24650403/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-28 03:09:52  来源:igfitidea点击:

solving $rootScope:infdig Infinite $digest Loop

javascriptangularjsqangular-promise

提问by lostdorje

I get the basic idea of the infinite digest loop and how it happens, but I'm running into the problem. Here is a fiddle demonstrating my code and problem:

我了解了无限摘要循环的基本概念以及它是如何发生的,但我遇到了问题。这是一个演示我的代码和问题的小提琴:

http://jsfiddle.net/eS5e5/1/

http://jsfiddle.net/eS5e5/1/

In the jsfiddle console you'll see the infinite digest loop.

在 jsfiddle 控制台中,您将看到无限摘要循环。

Basically I have to make decisions on data that may not have loaded yet so I need to wait for the promise to resolve using then(). I have a promise called user. There are two different places in the code where I call then() on user.

基本上我必须对可能尚未加载的数据做出决定,所以我需要等待使用 then() 解决的承诺。我有一个叫用户的承诺。代码中有两个不同的地方,我对用户调用 then()。

  1. Just after I define it. I need to set a scope variable based on it.
  2. In another scope method, $scope.isAdmin()
  1. 就在我定义它之后。我需要根据它设置一个范围变量。
  2. 在另一个范围方法中, $scope.isAdmin()

For number 2, it might be asked why I just don't use $scope.user directly in the $scope.isAdmin() method. The problem is, it's possible for $scope.isAdmin() to be called before the async request for the user returns, in which case I need to 'block' before returning from $scope.isAdmin().

对于第 2 点,可能会问为什么我不直接在 $scope.isAdmin() 方法中使用 $scope.user。问题是,$scope.isAdmin() 可能在用户的异步请求返回之前被调用,在这种情况下,我需要在从 $scope.isAdmin() 返回之前“阻止”。

My question is, what about $scope.isAdmin() is making angular think that a 'watched' variable has changed and that the digest cycle needs to run again?

我的问题是,$scope.isAdmin() 使 angular 认为“监视”变量已更改并且摘要循环需要再次运行怎么办?

$scope.isAdmin() isn't actually changing anything.

$scope.isAdmin() 实际上并没有改变任何东西。

Here is the stripped down code:

这是精简后的代码:

HTML:

HTML:

<body ng-controller='myController'>  
  <div ng-if='isAdmin()'>Hi! <strong>{{ user.username }}</strong> is an Admin!!!</div>
  <div ng-if='!isAdmin()'>Hi! <strong>{{ user.username }}</strong> is NOT an Admin!!!</div>
</body>

And the JS:

和 JS:

angular.module('myApp', [])
  .factory('myService', function($q, $timeout) {
    return {        
      getUser: function() {
        var deferred = $q.defer();

        $timeout(function() {
          deferred.resolve({ username: 'me', isAdmin: true });
        }, 2000);

        return deferred.promise;
      }
    };
  })
  .controller('myController', function($scope, $q, myService) {      
    var getUserDeferred = $q.defer();
    var user = getUserDeferred.promise;
    user.then(function(user) {
      $scope.user = user;
      return user;
    });

    $scope.getUser = function() {
      return myService.getUser().then(function(user) {
        getUserDeferred.resolve(user);
      });
    };

    $scope.isAdmin = function() {
      return user.then(function(user) {
        return user.isAdmin;
      });
    };

    $scope.getUser();
  });

回答by lostdorje

So I finally figured out my own problem and thought I would answer it for others in case someone else may find this info useful.

所以我终于想出了我自己的问题,并认为我会为其他人回答,以防其他人发现这些信息有用。

The crux of the fix had to do with 2 concepts: angular promises andangular watches. By being aware of and applying the 2 concepts together the fix was actually pretty simple.

修复的关键与两个概念有关:angular promisesangular watch。通过了解并应用这两个概念,修复实际上非常简单。

Everything you put on $scope is 'watched' including functions. Every time something watched changes $scope.$apply() runs again to apply the changes. If a scope function (eg: $scope.isAdmin()) changes its return value from one 'apply' to the next it will trigger another 'apply', until things stabilize and the return value isn't changing.

您放在 $scope 上的所有内容都被“监视”,包括函数。每次观察到的变化 $scope.$apply() 都会再次运行以应用更改。如果范围函数(例如:$scope.isAdmin())将其返回值从一个“应用”更改为下一个,它将触发另一个“应用”,直到事情稳定下来并且返回值没有改变。

But in my code I was returning user.then(...) which just returns a new promise (which kept the apply cycle going on forever since the return value kept changing).

但是在我的代码中,我正在返回 user.then(...) ,它只返回一个新的承诺(由于返回值不断变化,因此应用循环永远持续下去)。

In my isAdmin() function I needed to defer its return value until the user actually loaded (any other return value would be meaningless). So I changed the code to check if the user async call had resolved by checking $scope.user and if so returning a valid isAdmin value. If $scope.user was still not defined I would just return the promise I already created.

在我的 isAdmin() 函数中,我需要推迟它的返回值,直到用户实际加载(任何其他返回值都没有意义)。因此,我更改了代码,通过检查 $scope.user 来检查用户异步调用是否已解决,如果是,则返回有效的 isAdmin 值。如果 $scope.user 仍未定义,我只会返回我已经创建的承诺。

I changed the $scope.isAdmin() to be:

我将 $scope.isAdmin() 更改为:

$scope.isAdmin = function() {
  if ($scope.user) {
    return $scope.user.isAdmin;
  }

  return user;
};

This has the same effect as the original code without triggering an infinite apply cycle. Specifically, if the $scope.user has not resolved we still return a promise as before, by returning the user var. Note however that the user var is the same promise not a new one created by then() so the apply cycle stabilizes.

这与原始代码具有相同的效果,而不会触发无限应用循环。具体来说,如果 $scope.user 没有解析,我们仍然像以前一样返回一个 promise,通过返回用户变量。但是请注意,用户变量是相同的承诺,而不是由 then() 创建的新承诺,因此应用周期稳定。

And just for completeness here is the updated jsfiddle:

为了完整起见,这里是更新的 jsfiddle:

http://jsfiddle.net/eS5e5/2/

http://jsfiddle.net/eS5e5/2/