解决 $rootScope:infdig 无限 $digest 循环问题

8
我了解无限循环的基本概念以及如何发生,但我遇到了问题。这里是一个演示我的代码和问题的 fiddle:http://jsfiddle.net/eS5e5/1/。在 jsfiddle 控制台中,您将看到无限循环。
基本上,我必须根据可能尚未加载的数据做出决策,因此我需要使用 then() 等待 promise 解析。我有一个名为 user 的 promise。代码中有两个不同的地方调用 user 的 then()。
1. 定义后。我需要基于它设置作用域变量。 2. 在另一个作用域方法 $scope.isAdmin() 中。
对于第二点,可能会问为什么我不直接在 $scope.isAdmin() 方法中使用 $scope.user。问题是,$scope.isAdmin() 可能会在异步请求 user 返回之前被调用,在这种情况下,我需要在从 $scope.isAdmin() 返回之前“阻塞”。
我的问题是,$scope.isAdmin() 的哪个部分让 angular 认为已更改了'被观察' 的变量,并且需要重新运行 digest cycle? $scope.isAdmin() 实际上没有改变任何内容。
以下是简化后的代码:
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>

以下是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();
  });
1个回答

14

所以,我最终解决了自己的问题,并想为其他人回答该问题,以防其他人可能会发现这些信息有用。

解决方案的关键在于2个概念:Angular promises和Angular watches。通过了解并将这两个概念应用在一起,修复实际上非常简单。

您放在$scope上的所有内容都是“watched”,包括函数。每次观察到有变化时,$scope.$apply()都会再次运行以应用更改。如果作用域函数(例如:$scope.isAdmin())从一个'apply'到另一个更改其返回值,它将触发另一个'apply',直到事物稳定且返回值不再更改。

但是,在我的代码中,我返回了user.then(...),这只返回了一个新的promise(由于返回值不断变化,导致应用程序周期永远保持运行状态)。

在我的isAdmin()函数中,我需要延迟其返回值,直到用户实际加载完成(任何其他返回值都是无意义的)。因此,我更改了代码,检查$scope.user是否已解析异步调用,如果是,则返回有效的isAdmin值。如果$scope.user仍未定义,则只需返回我已创建的promise。

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

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

  return user;
};

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

完整的jsfiddle更新如下:

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


1
感谢您回答自己的问题 - 这是对我来说非常相关的部分,让我理解了我的问题: 但是在我的代码中,我是[... ],它只返回一个新的Promise(由于返回值不断变化而使应用程序循环无限进行)。 - keithl8041

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接