`

理解和解决angularJS报错$apply already in progress

阅读更多

  如果我们使用了AngularJS中的$scope.$apply()或者$scope.$digest(),我们很可能会遇到类似下面的错误,虽然这个错误没有太大影响,但是在日志中看起来还是很不爽的,日志中记录的异常或者错误,就应该是需要关注和解决的问题,否则就没有必要出现在日志中了。

Error: [$rootScope:inprog] $apply already in progress  
http://errors.angularjs.org/1.3.13/$rootScope/inprog?p0=%24apply  
    at angular.js:63  
    at beginPhase (angular.js:14755)  
    at Scope.$apply (angular.js:14499)  
    at new <anonymous> (1%20-%20%E5%89%AF%E6%9C%AC.html:10)  
    at Object.invoke (angular.js:4185)  
    at extend.instance (angular.js:8454)  
    at angular.js:7700  
    at forEach (angular.js:331)  
    at nodeLinkFn (angular.js:7699)  
    at compositeLinkFn (angular.js:7078)  

        下面这段代码就会报上面这个错误:

var myModule = angular.module('myModule', []);    
myModule.controller("ctrl_1",function($scope){  
  $scope.value = "aty";  
  $scope.$apply();  
  //$scope.$digest();  
});

  这个错误其实很好理解:angularjs框架本身已经在做脏数据检测了,我们没有必要再手动调用$apply或者$digest。这里自然而然出现了一个疑问:什么时候需要我们手动调用$apply或者$digest,什么时候不需要呢?这是个好问题,我现在也不知道,目前我只能列出自己在项目中遇到的2种需要手动调用$apply的情况。

情况一:

  controller中如果有异步操作,比如ajax回调,timeout延时等。可以这么理解:由于异步(延迟)的存在,当开始执行回调函数的时候,angularJS自身controller中的脏值检测已经结束,无法检测到回调函数导致数据的变化。

<html>  
  <head>  
    <script src="jquery-1.11.1.min.js"></script>  
    <script src="angular.js"></script>    
    <script>  
          
        var myModule = angular.module('myModule', []);    
        myModule.controller("ctrl_1",function($scope){  
            $scope.text = "place";  
              
            setTimeout(function(){  
                $scope.text = "value setted after time out";  
                $scope.$apply();//必需手动进行脏值检测,否则数据无法刷新到界面  
            },1000);  
          
        });   
          
        $(function(){  
            angular.bootstrap($("#div1")[0], ["myModule"]);   
              
        })  
    </script>  
  </head>  
  <body>  
    <div id="div1" ng-controller="ctrl_1">  
        <div>{{text}}</div>  
        <input id="btn" type="button" value="jquery-event"></input>       
    </div>    
  </body>  
</html>  

  这段代码如果没有调用$scope.$apply()的话,数据是不会刷新到界面上的。

 

情况二:

  在jQuery代码中修改$scope中的数据。这种情况是在angular框架之外操作$scope中的数据,angular不能检测到数据变化是正常的。

<html>  
  <head>  
    <script src="jquery-1.11.1.min.js"></script>  
    <script src="angular.js"></script>    
    <script>  
          
        var myModule = angular.module('myModule', []);    
        myModule.controller("ctrl_1",function($scope){  
            $scope.text = "place";  
        });   
          
        $(function(){  
            angular.bootstrap($("#div1")[0], ["myModule"]);   
  
            $("#btn").click(function(){  
                var $scope = $("#btn").scope();  
                $scope.text = "value setted in jquery";  
                $scope.$apply();  
            });           
        })  
    </script>  
  </head>  
  <body>  
    <div id="div1" ng-controller="ctrl_1">  
        <div>{{text}}</div>  
        <input id="btn" type="button" value="jquery-event"></input>       
    </div>    
  </body>  
</html>

  在JQuery的事件处理函数中,我们能够通过dom拿到其关联的$scope对象,进而修改$scope中的数据。这种情况下,也必需要手动调用$scope.$apply()。

  也就是说我们必须要清楚哪儿些情况需要手动$apply,哪儿些情况不需要手动$apply,这看起来很简单,实际上并不是这样的。看一段我们项目中的代码:

var myModule = angular.module('myModule', []);    
myModule.controller("ctrl_1",function($scope){  
    $scope.listItems = [];  
      
    $scope.loadListFromService = function(){  
        Spl.MessageProcessor.loadData({  
                    serviceId : "url",  
                    data : {},  
                    success : function(json) {  
                        $scope.listItems = json.results;  
            // 要不要$scope.$apply()?  
                    },  
                    error: function() {  
                        console.error("invoke service["+optionsJson.serviceId+"] error.");  
                    }  
        });  
      
    }  
      
    $scope.loadListFromService();  
      
});  

  loadData()这个函数很像ajax回调,确实是这样的,这个API不过是做了一点封装,大致代码如下:

function loadData(options)  
{  
    // 从本地缓存中读取,很快  
    var dataInCache = U.loadDataFromCache(options.serviceId);  
    if(dataInCache)  
    {  
        options.success(dataInCache);  
    }  
    else  
    {  
        //异步ajax  
        U.readDataFromServer(options.serviceId, options.data, function(response){  
            options.success(response);  
        });  
    }  
}  

  由于缓存的影响,$scope.loadListFromService()变得不再那么可控,如果本地已经有缓存了,那直接读取缓存是很快的,这个时候不需要手动$apply;如果是第一次,本地没有缓存,那么就变成了情况1,我们需要手动$apply。显然调用loadData()函数的地方,不需要也不应该关注到底有没有缓存的存在,这个时候判断需要不要手动$apply就没有那么容易的。简单粗暴的方式,不管怎么样都手动调用下$scope.$apply(),这样功能不会有问题,但是日志中就不可避免要出现文中开头提到的错误。

  angular中的$scope中提供了一个$$phase变量,如果这个变量的值是"$digest" 或者"$apply",就代表angular自身已经在做脏值检测了,不需要我们再去调用$apply或者$digest;否则的话就需要我们手动调用$apply或者$digest了。利用这个属性,我们就可以方便解决上面的错误了,判断一下呗。下面是一个工具函数,好理解吧。

function safeApply(scope, fn) {  
    (scope.phase||scope.$root.phase) ? fn() : scope.$apply(fn);  
}

  最后提一下,$digest、$apply、$$phase这些属性或者方法其实都是$scope中的私有的,最好不要使用。如果你用到了这些方法,基本可以断言你的代码存在问题,没有按照angular的方式来组织代码。比如情况1中的setTimeout,完全可以用angular中的$timeout代替,这才是推荐的方式,而不是通过$apply来补救。

<html>  
  <head>  
    <script src="jquery-1.11.1.min.js"></script>  
    <script src="angular.js"></script>    
    <script>  
          
        var myModule = angular.module('myModule', []);    
        myModule.controller("ctrl_1",function($scope, $timeout){  
            $scope.text = "place";  
              
            $timeout(function(){  
                $scope.text = "value setted after time out";  
            },1000);  
          
        });   
          
        $(function(){  
            angular.bootstrap($("#div1")[0], ["myModule"]);   
              
        })  
    </script>  
  </head>  
  <body>  
    <div id="div1" ng-controller="ctrl_1">  
        <div>{{text}}</div>  
        <input id="btn" type="button" value="jquery-event"></input>       
    </div>    
  </body>  
</html>

  所以解决“$apply already in progress”最好的方式,就是不要使用$scope.$apply()或者$scope.$digest()。

 

参考文章:Prevent error $digest already in progress when calling $scope.$apply()

分享到:
评论

相关推荐

    AngularJS报错$apply already in progress的解决方法分析

    本文实例分析了AngularJS报错$apply already in progress的解决方法。分享给大家供大家参考,具体如下: 如果我们使用了AngularJS中的$scope.$apply()或者$scope.$digest(),我们很可能会遇到类似下面的错误,虽然这...

    AngularJS中$apply方法和$watch方法用法总结

    本文实例总结了AngularJS中$apply方法和$watch方法用法。分享给大家供大家参考,具体如下: 引言 最近在项目中封装控件的时候用到了$watch方法来监听module中的值的变化,当时小编对这个方法不是很了解,所以在网上...

    angularjs 中$apply,$digest,$watch详解

    在AngularJS中,$apply,$digest和$watch是核心的脏检查机制,它们共同确保了模型和视图之间的同步。下面将详细解释这三个概念及其工作原理。 1. **$apply (通知)** `$apply`是AngularJS提供的一个方法,它的主要...

    angularJS中$apply()方法详解

    AngularJS是Google开发的一个流行的前端JavaScript框架,用于构建动态网页应用。在AngularJS中,$apply()是...通过理解$apply(),开发者可以更加高效地管理数据和视图之间的同步,构建出响应迅速且用户友好的Web应用。

    理解$watch ,$apply 和 $digest --- 理解数据绑定过程

    理解$watch,$apply和$digest的工作原理对于优化AngularJS应用性能至关重要。通过避免不必要的$apply调用,减少$watch的数量,以及合理安排异步操作,可以显著提高应用的响应速度。 总结起来,$watch用于监听模型...

    Angularjs中的$apply及优化使用详解

    前言 对于一个在前端属于纯新手的我来说,Javascript都还是一知半解,要想直接上手angular JS,遇到的阻力还真是不少。...Error: $digest already in progress 那么,导致这些现象的原因时什么的呢?$a

    AngularJS-Scope.SafeApply:AngularJS $ scope。$ apply()没有麻烦和错误

    $ apply()没有麻烦和错误:) 关于 AngularJS的核心功能是范围,变量和绑定的使用。 在代码级别更新值并回显DOM或应用程序的过程围绕着对范围运行摘要来查看发生了什么变化以及什么仍然是相同的。 为了运行摘要,...

    全面解析Angular中$Apply()及$Digest()的区别

    $apply()和$digest()在AngularJS中是两个核心概念,但是有时候它们又让人困惑。而为了了解AngularJS的工作方式,首先需要了解$apply()和$digest()是如何工作的。这篇文章旨在解释$apply()和$digest()是什么,以及在...

    AngularJS双向数据绑定原理之$watch、$apply和$digest的应用

    AngularJS作为一款前端JavaScript框架,在处理数据绑定方面有着自己独特的机制。...AngularJS通过这些机制,巧妙地解决了浏览器在数据交互方面的局限性,提供了一种高效、动态的数据绑定解决方案。

    浅谈关于angularJs中使用$.ajax的注意点

    需要注意的是,$scope.$apply()会在当前作用域及其子作用域上运行$digest循环,如果在已运行的$digest循环中再次调用,会导致“$apply already in progress”错误。因此,确保$.ajax的回调在$digest安全的环境中执行...

    angularJS 中$scope方法使用指南

    AngularJS是Google推出的一款前端JS框架,是MVW(Model-View-Whatever)框架的一种,它将应用程序分解为模型(model)、视图(view)和控制器(controller),便于模块化开发。在AngularJS中,$scope是控制器的载体,是...

    AngularJs定时器$interval 和 $timeout详解

    AngularJS作为一款流行的前端JavaScript框架,提供了许多有用的服务,其中包括了$interval和$timeout服务,这两个服务能够让我们在AngularJS应用程序中实现定时器功能。 首先,我们来讲解$interval服务。$interval...

    AngularJS中的$watch(),$digest()和$apply()区分

    AngularJS是一款由Google开发的开源JavaScript框架,广泛应用于单页面应用程序的开发中。在AngularJS中,数据绑定是其核心特性之一...理解这三个函数的工作机制,对于高效使用AngularJS和开发复杂的应用程序至关重要。

    说说AngularJS中的$parse和$eval的用法

    在AngularJS中,$parse和$eval是两个关键的服务,它们与AngularJS的表达式处理密切相关。理解这两个服务的工作原理对于深入掌握AngularJS的绑定机制至关重要。 首先,我们需要明确什么是AngularJS表达式。这些...

Global site tag (gtag.js) - Google Analytics