$ apply已在执行中错误


133

堆栈跟踪:

Error: $apply already in progress
at Error (<anonymous>)
at beginPhase (file:///android_asset/www/built.min.js:7:22740)
at Object.Scope.$apply (file:///android_asset/www/built.min.js:7:25967)
at navigator.geolocation.getCurrentPosition.that (file:///android_asset/www/built.min.js:13:8670)
at Object.geolocation.getCurrentPosition (file:///android_asset/www/plugins/org.apache.cordova.core.geolocation/www/geolocation.js:122:13)
at Object.getCurrentPosition (file:///android_asset/www/built.min.js:13:8589)
at Object.getCurrentPosition (file:///android_asset/www/built.min.js:13:8277)
at Object.getCurrentCity (file:///android_asset/www/built.min.js:13:8941)
at Object.$scope.locateDevice (file:///android_asset/www/built.min.js:13:10480)
at file:///android_asset/www/built.min.js:7:12292:7

参考此代码http://pastebin.com/B9V6yvFu

    getCurrentPosition: cordovaReady(function (onSuccess, onError, options) {

        navigator.geolocation.getCurrentPosition(function () {
            var that = this,
                args = arguments;

            if (onSuccess) {
                $rootScope.$apply(function () {
                    onSuccess.apply(that, args);
                });
            }
        }, function () {
            var that = this,
                args = arguments;
            if (onError) {
                $rootScope.$apply(function () {
                    onError.apply(that, args);
                });
            }
        }, {
            enableHighAccuracy: true,
            timeout: 20000,
            maximumAge: 18000000
        });
    })

奇怪的是,在我的LG4X上它可以正常工作,但是在我的三星s2上它会抛出上述错误。有什么想法怎么了?


1
您是否尝试过stackoverflow.com/a/12859093/1266600?可能是因为不同的设备->不同的处理速度->不同的时序,这可能会在某些地方引起冲突,而在其他地方则不会。
sushain97 2013年

20
使用$timeout()
OnurYıldırım2014年

7
+1到$ timeout()注释。请参阅:stackoverflow.com/questions/12729122/...
特雷沃-

Answers:


106

因为$apply在现有的消化周期内进行调用,所以收到此错误。

最大的问题是:您为什么打电话$apply$apply除非您是从非Angular事件进行接口的,否则您根本不需要调用。$apply通常的存在意味着我做错了事(除非,再次,$ apply来自非Angular事件)。

如果$apply确实合适,请考虑使用“安全应用”方法:

https://coderwall.com/p/ngisma


41
链接的安全应用的核心是一个反模式(根据文档)github.com/angular/angular.js/wiki/Anti-Patterns。如果要使用将来支持的方法($$ phase将消失!),请将代码包装在$ timeout()中,而无需设置时间。当前摘要周期完成后,它将安全地应用。
betaorbust 2014年

@betaorbust同意。安全申请是不好的。同样,调用多次会导致性能问题。最好对代码进行结构化设计,以避免一起出现问题。
Brian Genisio 2014年

我没有打电话给apply
电路


41

您可以使用以下语句:

if ($scope.$root.$$phase != '$apply' && $scope.$root.$$phase != '$digest') {
    $scope.$apply();
}

1
不建议使用以$$开头的变量,因为它们是私有的。在这种情况下,$$ phase
Ara Yeressian

9
这个答案比上面的答案更有帮助。我需要一个解决方案,不要因为可能无法控制的事情而受到警告。我们混合了角度代码和遗留代码,它们必须以某种方式进行交互。仅重写所有遗留代码太昂贵了……
Jordan Lapp

24

如果在某些情况下必须应用范围,则可以设置超时,以便将$ apply推迟到下一个刻度

setTimeout(function(){ scope.$apply(); });

或将您的代码包装在$ timeout(function(){..})中;因为它会在执行结束时自动$ apply作用域。如果您需要函数同步运行,我将首先执行。


我发现我需要将动作包含在setTimeout(function() { $apply(function() {... do stuff ...} ) })下面的@Tamil Vendhan中。
原型

6
不要使用setTimeout,这只会产生另一个$ apply的需求。使用该框架,它具有$ timeout服务,可以为您完成所有操作。
斯宾塞2014年

10

就我而言,我使用$apply角度日历UI来链接某些事件:

$scope.eventClick = function(event){           
    $scope.$apply( function() {
        $location.path('/event/' + event.id);
    });
};

阅读问题文档后:https : //docs.angularjs.org/error/ $ rootScope / inprog

不一致的API(同步/异步)部分非常有趣:

例如,假设一个第三方库具有一种可以为我们检索数据的方法。由于它可能正在对服务器进行异步调用,因此它接受回调函数,该函数将在数据到达时被调用。

由于MyController构造函数始终从$ apply调用中实例化,因此我们的处理程序正尝试从一个$ apply调用中输入新的$ apply块。

我将代码更改为:

$scope.eventClick = function(event){           
    $timeout(function() {
        $location.path('/event/' + event.id);
    }, 0);
};

奇迹般有效 !

在这里,我们使用$ timeout将更改的范围安排在将来的调用堆栈中。通过提供0ms的超时时间,这将尽快发生,并且$ timeout将确保在单个$ apply块中调用代码。


1
您的$ timeout delay 0解决方案很棒。
阿桑(Ahsan)

9

我认为在angular 1.3中,他们添加了一个新功能- $scope.$applyAsync()。此函数调用稍后应用-他们至少说大约10毫秒。它不是完美的,但至少可以消除烦人的错误。

https://docs.angularjs.org/api/ng/type/ $ rootScope.Scope#$ applyAsync



2

刚刚解决了这个问题。它记录在这里

$rootScope.$apply在同一流程中打了两次电话。我所做的只是用来包装服务功能的内容setTimeout(func, 1)


1

我知道这是一个老问题,但是如果您真的需要使用$ scope。$ applyAsync();,


0

我叫$ scope。$ apply这样忽略一次调用多个。

      var callApplyTimeout = null;
      function callApply(callback) {
          if (!callback) callback = function () { };
          if (callApplyTimeout) $timeout.cancel(callApplyTimeout);

          callApplyTimeout = $timeout(function () {
              callback();
              $scope.$apply();
              var d = new Date();
              var m = d.getMilliseconds();
              console.log('$scope.$apply(); call ' + d.toString() + ' ' + m);
          }, 300);
      }

只需致电

callApply();
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.