18

在 AngularJS 中处理 3rd 方工具和外部 DOM 事件时要记住的一件主要事情是使用$scope.$apply()方法操作来启动更改。这很有效,但有时范围本身已经在通过摘要(基本上是 $apply 方法触发的内容)并在此过程中调用 $apply 会引发错误。因此,要解决这个问题,您必须注意$scope.$$phase在进行摘要时设置为范围的标志。

所以现在,假设您想更改 URL 并启动:

$scope.$apply(function() {
  $location.path('/home');
});

这按预期工作,但现在让我们假设 $scope 正忙于做这件事。因此,您检查 $$phase 变量并假设您的更改将被拾取:

if($scope.$$phase) {
  $location.path('/home');
}
else {
  $scope.$apply(function() {
    $location.path('/home');
  });
}

这就是我一直在做的事情(显然不是代码重复),它似乎 100% 的时间都在工作。我担心的是,当范围处于消化过程中时,AngularJS 是如何拾取变化的?

也许这个例子不够具体。让我们假设一些更大的东西。想象一下,如果您有一个包含大量绑定的巨大网页,并假设消化将线性地浏览页面(我假设它在优先级方面做了类似的事情......在这种情况下,无论出现在首先是 DOM 树)并从上到下更新页面上的绑定。

<div class="binding">{{ binding1 }}</div>
<div class="binding">{{ binding2 }}</div>
<div class="binding">{{ binding3 }}</div>
<div class="binding">{{ binding4 }}</div>
<div class="binding">{{ binding5 }}</div>
<div class="binding">{{ binding6 }}</div>
<div class="binding">{{ binding7 }}</div>
<div class="binding">{{ binding8 }}</div>

让我们假设消化正在进行,并且它位于消化队列中间附近的某个地方。现在让我们尝试更改页面顶部某处的绑定值。

if($scope.$$phase) {
  $scope.binding1 = 'henry';
}

现在,不知何故,AngularJS 获取了更改并正确更新了绑定。尽管相对于 HTML/DOM,可以认为更改本身在队列中更早发生。

我的问题是 AngularJS 如何管理这种潜在的竞争条件?如果binding8更新(因为它位于页面下方),我会感到有些舒服,但是因为binding1也会更新(立即无需再次调用 $apply),这让我有点迷茫。这是否意味着在两者之间的某个地方又进行了一次消化?还是 $scope 对象比我想象的更神奇?我会假设这个问题之前已经存在,但是由于首先找出 $$phase 和 $scope 有点棘手,所以我假设这也可能是一些从裂缝中消失的东西。

有任何想法吗?

4

3 回答 3

19

关于绑定和竞争条件。$digest将循环所有观察者,直到没有变化。正如您可以通过将日志添加到观察者/绑定方法来观察到的,它将至少调用每个绑定/观察者两次,以确保没有更改并且所有绑定值都是稳定的。这些只是脏检查,直到每个值都被解决(在 2 次循环迭代中没有改变)。希望有帮助。

这在此处的 AngularJS 文档中进行了解释:http ://docs.angularjs.org/api/ng.$ro​​otScope.Scope#$digest

注意:这是 matsko 要求的我评论的副本/粘贴。

于 2012-11-30T16:19:15.107 回答
1

Apply会一直打电话digest,直到确定一切都没有改变。所以 if 可能在第一次调用时有竞争条件,但第二个总是会补偿。

于 2012-11-30T15:14:35.483 回答
0

无论如何,您都不应该使用该解决方法:如果您的状态$scope不确定此函数,您应该在调用堆栈上消化它更高的位置,您知道您是否被 angular 同步处理,因此不需要消化,或者如果您'正在异步修改模型,以便您可以消化它。

附带说明一下,$scope.$apply基本上是$scope.$root.$digest带有错误处理的,因此如果您要在指令中更新隔离范围,则可以通过调用$digest而不是来保存性能$apply

// Somewhere in a callback
$location.path('/home');
$scope.$digest();

PS:这是为了纠正 e-satis 答案:$digest在范围脏时也会调用自己。

于 2014-04-10T22:41:32.280 回答