溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

AngularJS如何解決報$apply already in progress錯誤的問題

發(fā)布時間:2021-07-12 11:43:59 來源:億速云 閱讀:169 作者:小新 欄目:web開發(fā)

這篇文章主要介紹了AngularJS如何解決報$apply already in progress錯誤的問題,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。

具體如下:

如果我們使用了AngularJS中的$scope.$apply()或者$scope.$digest(),我們很可能會遇到類似下面的錯誤,雖然這個錯誤沒有太大影響,但是在日志中看起來還是很不爽的,日志中記錄的異?;蛘咤e誤,就應(yīng)該是需要關(guān)注和解決的問題,否則就沒有必要出現(xiàn)在日志中了。

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();
});

這個錯誤其實(shí)很好理解:angularJS框架本身已經(jīng)在做臟數(shù)據(jù)檢測了,我們沒有必要再手動調(diào)用$apply或者$digest。這里自然而然出現(xiàn)了一個疑問:什么時候需要我們手動調(diào)用$apply或者$digest,什么時候不需要呢?這是個好問題,我現(xiàn)在也不知道,目前我只能列出自己在項(xiàng)目中遇到的2種需要手動調(diào)用$apply的情況。

情況1:controller中如果有異步操作,比如ajax回調(diào),timeout延時等??梢赃@么理解:由于異步(延遲)的存在,當(dāng)開始執(zhí)行回調(diào)函數(shù)的時候,angularJS自身controller中的臟值檢測已經(jīng)結(jié)束,無法檢測到回調(diào)函數(shù)導(dǎo)致數(shù)據(jù)的變化。

<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();//必需手動進(jìn)行臟值檢測,否則數(shù)據(jù)無法刷新到界面
      },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>

這段代碼如果沒有調(diào)用$scope.$apply()的話,數(shù)據(jù)是不會刷新到界面上的。

情況2:在jQuery代碼中修改$scope中的數(shù)據(jù)。這種情況是在angular框架之外操作$scope中的數(shù)據(jù),angular不能檢測到數(shù)據(jù)變化是正常的。

<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的事件處理函數(shù)中,我們能夠通過dom拿到其關(guān)聯(lián)的$scope對象,進(jìn)而修改$scope中的數(shù)據(jù)。這種情況下,也必需要手動調(diào)用$scope.$apply()。

也就是說我們必須要清楚哪兒些情況需要手動$apply,哪兒些情況不需要手動$apply,這看起來很簡單,實(shí)際上并不是這樣的??匆欢挝覀冺?xiàng)目中的代碼:

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()這個函數(shù)很像ajax回調(diào),確實(shí)是這樣的,這個API不過是做了一點(diǎn)封裝,大致代碼如下:

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()變得不再那么可控,如果本地已經(jīng)有緩存了,那直接讀取緩存是很快的,這個時候不需要手動$apply;如果是第一次,本地沒有緩存,那么就變成了情況1,我們需要手動$apply。顯然調(diào)用loadData()函數(shù)的地方,不需要也不應(yīng)該關(guān)注到底有沒有緩存的存在,這個時候判斷需要不要手動$apply就沒有那么容易的。簡單粗暴的方式,不管怎么樣都手動調(diào)用下$scope.$apply(),這樣功能不會有問題,但是日志中就不可避免要出現(xiàn)文中開頭提到的錯誤。

angular中的$scope中提供了一個$$phase變量,如果這個變量的值是"$digest" 或者"$apply",就代表angular自身已經(jīng)在做臟值檢測了,不需要我們再去調(diào)用$apply或者$digest;否則的話就需要我們手動調(diào)用$apply或者$digest了。利用這個屬性,我們就可以方便解決上面的錯誤了,判斷一下唄。下面是一個工具函數(shù),好理解吧。

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

最后提一下,$digest、$apply、$$phase這些屬性或者方法其實(shí)都是$scope中的私有的,最好不要使用。如果你用到了這些方法,基本可以斷言你的代碼存在問題,沒有按照angular的方式來組織代碼。比如情況1中的setTimeout,完全可以用angular中的$timeout代替,這才是推薦的方式,而不是通過$apply來補(bǔ)救。

<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()。

感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享的“AngularJS如何解決報$apply already in progress錯誤的問題”這篇文章對大家有幫助,同時也希望大家多多支持億速云,關(guān)注億速云行業(yè)資訊頻道,更多相關(guān)知識等著你來學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI