141

堆栈跟踪:

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 上却抛出了上述错误。有什么想法有什么问题吗?

4

12 回答 12

108

您收到此错误是因为您正在调用$apply在现有消化循环内调用。

最大的问题是:你为什么打电话$apply$apply除非您从非 Angular 事件进行交互,否则您永远不需要调用。的存在$apply通常意味着我做错了什么(除非再次 $apply 发生在非 Angular 事件中)。

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

https://coderwall.com/p/ngisma

于 2013-09-05T00:52:31.460 回答
58

只需使用$evalAsync而不是$apply.

于 2014-10-23T12:39:32.263 回答
42

您可以使用以下语句:

if ($scope.$root.$$phase != '$apply' && $scope.$root.$$phase != '$digest') {
    $scope.$apply();
}
于 2013-09-05T10:51:10.007 回答
24

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

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

或将您的代码包装在 $timeout(function(){ .. }); 因为它会在执行结束时自动 $apply 范围。如果你需要你的函数同步运行,我会做第一个。

于 2013-09-05T02:33:31.877 回答
10

在我的情况下,我使用$apply角度日历 UI 来链接一些事件:

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

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

Inconsistent API (Sync/Async)部分非常有趣:

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

由于 MyController 构造函数总是在 $apply 调用中实例化,我们的处理程序试图从其中输入一个新的 $apply 块。

我将代码更改为:

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

奇迹般有效 !

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

于 2016-02-01T11:07:27.130 回答
9

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

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

于 2015-11-05T01:52:02.120 回答
3

在任何时间点,只能有一个$digest$apply正在进行的操作。这是为了防止很难检测到的错误进入您的应用程序。此错误的堆栈跟踪允许您跟踪导致错误的当前执行$apply$digest调用的来源。

更多信息: https ://docs.angularjs.org/error/$rootScope/inprog?p0=$apply

于 2014-10-08T03:55:49.260 回答
2

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

$rootScope.$apply我在同一个流程中调用了两次。我所做的只是用setTimeout(func, 1).

于 2013-10-30T19:26:37.027 回答
2

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

于 2017-07-01T05:22:30.037 回答
0

$apply只有在$apply尚未进行时,您才能进行更改。您可以将代码更新为

if(!$scope.$$phase) $scope.$apply();
于 2021-01-07T02:54:51.807 回答
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();
于 2016-10-06T10:09:53.837 回答
0

在这种情况下我们可以使用setTimeout函数。

console.log('primary task');

setTimeout(function() {
  
  console.log('secondary task');

}, 0);

这将确保在主要任务执行完成时执行次要任务。

于 2020-11-09T12:15:03.890 回答