首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >AngularJS :在调用$scope.$apply()时防止已经在进行中的错误$digest

AngularJS :在调用$scope.$apply()时防止已经在进行中的错误$digest
EN

Stack Overflow用户
提问于 2012-10-04 14:07:32
回答 27查看 460.2K关注 0票数 863

我发现我需要手动更新我的页面到我的范围越来越多,因为构建一个应用程序的角度。

我知道的唯一方法就是从控制器和指令的范围调用$apply()。这样做的问题是,它不断向控制台抛出一个错误,该控制台读取:

错误:$digest已经在进行中

有没有人知道如何避免这一错误,或者以不同的方式实现相同的目标?

EN

回答 27

Stack Overflow用户

发布于 2013-09-25 04:06:19

根据最近与角质人就这个主题进行的讨论:出于未来的考虑,您不应该使用$$phase

当按下“正确”的方法做这件事时,答案是

代码语言:javascript
运行
复制
$timeout(function() {
  // anything you want can go here and will safely be run on the next digest.
})

最近,我在编写棱角服务来包装facebook、google和twitter时遇到了这种情况,这些API在不同程度上都有回调。

下面是服务内部的一个示例。(为了简洁起见,服务的其余部分--设置变量、注入$timeout等等--已经停止。)

代码语言:javascript
运行
复制
window.gapi.client.load('oauth2', 'v2', function() {
    var request = window.gapi.client.oauth2.userinfo.get();
    request.execute(function(response) {
        // This happens outside of angular land, so wrap it in a timeout 
        // with an implied apply and blammo, we're in action.
        $timeout(function() {
            if(typeof(response['error']) !== 'undefined'){
                // If the google api sent us an error, reject the promise.
                deferred.reject(response);
            }else{
                // Resolve the promise with the whole response if ok.
                deferred.resolve(response);
            }
        });
    });
});

请注意,$timeout的延迟参数是可选的,如果未设置,默认为0 ($timeout调用$browser.defer哪个如果没有设置延迟,默认为0)

有点不直观,但这是男人写角度的答案,所以对我来说已经足够好了!

票数 679
EN

Stack Overflow用户

发布于 2013-07-30 22:51:27

摘要周期是一个同步调用。在完成之前,它不会将控制权让给浏览器的事件循环。有几种方法可以解决这个问题。处理这一问题的最简单方法是使用内置的$timeout,第二种方法是如果使用下划线或存档(应该是),调用以下命令:

代码语言:javascript
运行
复制
$timeout(function(){
    //any code in here will automatically have an apply run afterwards
});

或者如果你有房客:

代码语言:javascript
运行
复制
_.defer(function(){$scope.$apply();});

我们尝试了几种解决方案,我们讨厌将$rootScope注入到所有控制器、指令甚至一些工厂中。因此,到目前为止,$timeout和_.defer一直是我们的最爱。这些方法成功地告诉角直到下一个动画循环,这将保证当前范围。$apply已经结束。

票数 332
EN

Stack Overflow用户

发布于 2014-04-16 06:59:44

这里的许多答案包含了很好的建议,但也可能导致混淆。简单地使用$timeout不是最好的解决方案,也不是正确的解决方案。此外,如果您关心性能或可伸缩性,请务必阅读。

你应该知道的事

  • $$phase对框架是私有的,这是有充分理由的。
  • $timeout(callback)将等待当前摘要周期(如果有的话)完成,然后执行回调,然后在结束时运行一个完整的$apply
  • $timeout(callback, delay, false)也会这样做(在执行回调之前有一个可选的延迟),但是不会触发一个$apply (第三个参数),如果您不修改角度模型($scope),这会节省性能。
  • $scope.$apply(callback)除其他外调用$rootScope.$digest,这意味着它将重新消化应用程序的根范围及其所有子级,即使您是在一个独立的范围内。
  • $scope.$digest()将简单地将其模型同步到视图,但不会消化它的父作用域,这可以在使用隔离作用域(大部分来自指令)处理HTML的孤立部分时节省大量性能。$digest不接受回调:您执行代码,然后消化。
  • $scope.$evalAsync(callback)是在angularjs1.2中引入的,可能会解决大部分问题。请参考最后一段来了解更多。
  • 如果您得到了$digest already in progress error,那么您的体系结构是错误的:要么您不需要重新消化您的范围,要么您不应该负责这个问题(参见下面)。

如何构造代码

当您得到这个错误时,您正在尝试消化您的作用域,而它已经在进行中:由于您不知道您的作用域在那时的状态,所以您不负责处理它的消化。

代码语言:javascript
运行
复制
function editModel() {
  $scope.someVar = someVal;
  /* Do not apply your scope here since we don't know if that
     function is called synchronously from Angular or from an
     asynchronous code */
}

// Processed by Angular, for instance called by a ng-click directive
$scope.applyModelSynchronously = function() {
  // No need to digest
  editModel();
}

// Any kind of asynchronous code, for instance a server request
callServer(function() {
  /* That code is not watched nor digested by Angular, thus we
     can safely $apply it */
  $scope.$apply(editModel);
});

如果您知道自己在做什么,并且在大角度应用程序中使用一个孤立的小指令,那么您可以选择$digest而不是$apply来保存性能。

自Angularjs 1.2以来的更新

在任何$scope:$evalAsync中都添加了一个新的、强大的方法。基本上,如果出现一个摘要周期,它将在当前摘要周期内执行回调,否则一个新的摘要周期将开始执行回调。

如果您真的知道您只需要同步HTML中的一个独立部分(因为如果没有正在进行的话,就会触发一个新的$apply ),那么这仍然不如$apply好,但是当您正在执行一个您不知道是否将同步执行的函数时,这是最好的解决方案,例如在获取可能缓存的资源之后:有时这将需要对服务器进行异步调用,否则资源将在本地同步获取。

在这些情况下和所有其他有!$scope.$$phase的地方,一定要使用$scope.$evalAsync( callback )

票数 273
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/12729122

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档