gpt4 book ai didi

javascript - Angular为什么不忽略随后的$ digest调用?

转载 作者:搜寻专家 更新时间:2023-11-01 05:12:12 25 4
gpt4 key购买 nike

以下必须是Angular.js开发人员必须在某些时候进行故障排除的最常见错误。

Error: $apply already in progress



有时,根本不需要 $scope.$apply调用,将其删除即可解决此问题。在其他时候,可能需要它,因此开发人员采用以下模式。
if(!$scope.$$phase) {
$scope.$apply();
}

我的问题是,当调用 $scope.$$phase循环或调用 return时,为什么Angular不会不做任何事情就只检查 $digest$scope.$apply,而不是抱怨并且 引发错误,因此很难隐式地跟踪

Angular难道不能在任何触发 $$phase的方法的入口点检查 $digest本身,从而使消费者不必麻烦吗?

除了触发不需要的手表以外,是否会遗留错位的 $scope.$apply调用,这有什么含义吗?

为什么 $$phase不检查默认值,而不检查奇数?

背景

我正在将Angular从 $$phase升级到 1.2.1,并且在页面加载时会抛出该异常,所以我不知道它的起源。因此,问题是,为什么我们甚至不得不追踪这种晦涩的bug,却不知道在代码中寻找该bug的原因?

最佳答案

如果收到此错误,则说明您在wrong place中使用了$scope.$apply()。我认为此错误的原因如下:如果在错误的位置调用$apply,则可能无法完全理解其存在的原因,并且可能错过了实际应调用的位置。
如果允许这样的失误悄无声息地发生(例如,始终先测试$scope.$$phase),这将鼓励草率使用,并且人们可能会在“$apply覆盖率”方面遇到漏洞。与该错误消息相比,这种错误(在 View 中未反射(reflect)$scope的更改)要难得多,您应该可以通过检查堆栈跟踪来调试该错误消息(请参见下文)。
为什么$apply存在?$apply的目的是实现$scope和view之间自动的双向数据绑定(bind),这是Angular的主要功能之一。此功能要求所有可能包含$scope修改的代码(因此,基本上每条用户代码)都在调用堆栈中的$apply调用下运行。正确地执行此操作实际上并不那么复杂,但是我认为documented并不是很好。
主要的“问题”是Javascript可以具有任意数量的 Activity 调用堆栈,并且Angular不会自动收到有关新调用堆栈的通知。每当触发异步回调时(例如,单击,超时,文件访问,网络响应等),都会创建一个新的调用堆栈。想要在这样的回调函数中修改$scope是很常见的。如果相应的事件由Angular本身提供,那么一切都会正常进行。但是有时您会想要订阅“外部事件”。 Google Maps事件,例如:

function Controller($scope) {
$scope.coordinates = [];
//...
var map = new google.maps.Map(mapElement, mapOptions);
google.maps.event.addDomListener(map, 'dblclick', function (mouseEvent) {
$scope.coordinates.push(mouseEvent.latLng);
});
}
http://jsfiddle.net/mhelvens/XLPY9/1/
双击此示例的 map 不会触发 View 更新,因为坐标是从“少了 $scope.coordinates”调用堆栈添加到 $apply的。换句话说,Angular不了解Google Maps事件。
如何以及在哪里使用 $apply
我们可以使用 $scope.$apply()通知Angular事件:
function Controller($scope) {
//...
google.maps.event.addDomListener(map, 'dblclick', function (mouseEvent) {
$scope.$apply(function () {
$scope.coordinates.push(mouseEvent.latLng);
});
});
}
http://jsfiddle.net/mhelvens/XLPY9/2/
规则是在外部事件的每个回调函数中首先执行 。 “$apply already in progress”错误表明您未遵循此规则。如果您必须经常处理Google Maps事件,则可以将此示例代码包装在服务中:
app.factory('onGoogleMapsEvent', function ($rootScope) {
return function (element, event, callback) {
google.maps.event.addDomListener(element, event, function (e) {
$rootScope.$apply(function () { callback(e); });
});
};
});

function Controller($scope, onGoogleMapsEvent) {
//...
onGoogleMapsEvent(map, 'dblclick', function (mouseEvent) {
$scope.coordinates.push(mouseEvent.latLng);
});
}
http://jsfiddle.net/mhelvens/XLPY9/3/ onGoogleMapsEvent事件现在可以识别Angular了,如果可以的话,也可以称为“内部事件”(顺便说一句,我完全是在编造这些术语)。这使您的代码更具可读性,并让您在日常编程中忘记了$apply;只需调用包装器而不是原始事件订阅者即可。
对于许多事件,Angular已经为我们做到了这一点。以 $timeout 服务为例。
调试“$apply already in progress”错误
假设我使用了包装器,但是由于我心不在b,我仍然手动调用$apply:
onGoogleMapsEvent(map, 'dblclick', function (mouseEvent) {
$scope.$apply(function () { // Error: $apply already in progress
$scope.coordinates.push(mouseEvent.latLng);
});
});
http://jsfiddle.net/mhelvens/XLPY9/4/
$apply调用不遵循我们的放置规则(毕竟onGoogleMapsEvent事件不是外部事件;我们授予它们“内部”权限)。如果双击 map ,您会看到错误和堆栈跟踪信息出现在日志中:
Error: [$rootScope:inprog] $apply already in progress
...
at Scope.$apply (.../angular.js:11675:11)
at http://fiddle.jshell.net/mhelvens/XLPY9/4/show/:50:16
...
at Scope.$apply (.../angular.js:11676:23)
at dl.ondblclick (http://fiddle.jshell.net/mhelvens/XLPY9/4/show/:33:24)
...
我只留下了相关的行:引用Scope.$apply的行。收到此错误消息时,应该始终在堆栈上找到两个Scope.$apply调用(或Scope.$digest调用,它们具有相似的功能; $digest$apply调用)。
最上方的 call 指示我们在哪里得到错误。另一个指示原因。继续并在您的Javascript控制台打开的情况下运行Fiddle。在这种情况下,我们可以得出结论:“哦,这是一个“内部事件”订阅者,我可以删除手动的$apply调用”。但是在其他情况下,您可能会发现位于堆栈下方的调用的位置也不正确。
这就是为什么我认为该错误有用的原因。理想情况下,仅在完全不调用$apply时才会出现错误。但是,如果Angular可以做到这一点,则无需首先手动调用该函数。

关于javascript - Angular为什么不忽略随后的$ digest调用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20805534/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com