有时我需要在我的代码中使用 $scope.$apply
,有时它会抛出"digest already in progress"错误 . 所以我开始找到解决方法并找到了这个问题:AngularJS : Prevent error $digest already in progress when calling $scope.$apply() . 但是在评论中(以及角度维基上),您可以阅读:
不要做if(!$ scope . $$阶段)$ scope . $ apply(),这意味着你的$ scope . $ apply()在调用堆栈中不够高 .
所以现在我有两个问题:
-
为什么这是一个反模式呢?
-
如何安全地使用$ scope . $ apply?
防止“正在进行摘要”错误的另一个“解决方案”似乎是使用$ timeout:
$timeout(function() {
//...
});
这是要走的路吗?它更安全吗?所以这是真正的问题:我如何能够消除"digest already in progress"错误的可能性?
PS:我只使用$ scope . $ apply适用于非同步的非angularjs回调 . (据我所知,你必须使用$ scope . 如果你想要应用你的更改,请申请$)
6 回答
经过一番挖掘后,我能够解决使用
$scope.$apply
是否总是安全的问题 . 简短的回答是肯定的 .答案很长:
由于您的浏览器如何执行Javascript,因此两个摘要调用不可能发生碰撞 by chance .
因此,错误“已经在进行中的摘要”只能在一种情况下发生:当在另一个$ apply中发出$ apply时,例如:
这种情况可能会出现 if 我们在纯非angularjs回调中使用$ scope.apply,例如
setTimeout
的回调 . 所以下面的代码是100%防弹,有 no 需要做if (!$scope.$$phase) $scope.$apply()
即使这个是安全的:
什么是 NOT 安全(因为$ timeout - 就像所有angularjs助手一样 - 已经为你调用
$scope.$apply
):这也解释了为什么
if (!$scope.$$phase) $scope.$apply()
的使用是一种反模式 . 如果以正确的方式使用$scope.$apply
,则根本不需要它:例如,在setTimeout
之类的纯js回调中 .请阅读http://jimhoskins.com/2012/12/17/angularjs-and-apply.html以获取更详细的说明 .
现在绝对是一种反模式 . 我只是不应该访问
$$
前缀表示的内部API .你应该用
因为这是Angular ^ 1.4中的首选方法,并且特别是作为应用程序层的API公开 .
在任何情况下,当您的摘要正在进行并且您推送其他服务进行摘要时,它只会出现错误,即摘要已在进行中 . 所以要治愈这个你有两个选择 . 您可以检查正在进行的任何其他摘要,如轮询 .
First one
如果上述条件为真,则可以应用$ scope . $ apply otherwies not和
second solution 使用$ timeout
它不会让其他摘要开始直到$ timeout完成它的执行 .
scope.$apply
触发$digest
循环,这是双向数据绑定的基础$digest
循环检查对象,即附加到$scope
的模型(确切地说是$watch
),以评估它们的值是否已更改,如果它检测到更改,则需要采取必要步骤来更新视图 .现在当你使用
$scope.$apply
时遇到错误"Already in progress" so it is quite obvious that a $digest is running but what triggered it?ans - >每个
$http
次调用,所有ng-click,repeat,show,hide等触发$digest
循环以及每个$ SC76E的最差部分 .即说你的页面有4个控制器或指令A,B,C,D
如果每个属性中有4个
$scope
属性,则页面上总共有16个$ scope属性 .如果在控制器D中触发
$scope.$apply
,则$digest
循环将检查所有16个值!加上所有$ rootScope属性 .Answer--> 但是
$scope.$digest
在子级和相同范围上触发$digest
,因此它只会检查4个属性 . 因此,如果您确定D中的更改不会影响A,B,C,那么请使用$scope.$diges
t而不是$scope.$apply
.因此,即使用户有 not fired any event ,仅仅ng-click或ng-show / hide可能会触发超过100个属性的
$digest
循环!使用
$timeout
,这是推荐的方式 .我的方案是我需要根据从WebSocket收到的数据更改页面上的项目 . 因为它在Angular之外,没有$ timeout,唯一的模型将被更改,但不会更改视图 . 因为Angular不知道那条数据已被更改 .
$timeout
基本上告诉Angular在下一轮$ digest中进行更改 .我也尝试了以下内容并且有效 . 与我不同的是,$ timeout更清晰 .
我发现非常酷的解决方案:
注入你需要的地方: