(20)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  (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를 수동으로 호출해야 하는지, 언제 필요하지 않은가?이것은 좋은 문제입니다. 저도 지금 잘 모르겠습니다. 현재 프로젝트에서 직면한 두 가지 $apply를 수동으로 호출해야 하는 상황만 열거할 수 있습니다.
상황1:controller에서 비동기적인 조작이 있다면, 예를 들어ajax 리셋,timeout 시간 지연 등이다.이렇게 이해할 수 있다. 비동기(지연)의 존재로 인해 리셋 함수를 실행하기 시작했을 때angularJS 자체controller의 더러운 값 검출이 끝났고 리셋 함수가 데이터의 변화를 검출할 수 없다.

  
	
    	
	
		
        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"]); 
			
		})
	
  
  
	
{{text}}

이 코드가 $scope.$를 호출하지 않으면apply () 를 사용하면 데이터가 인터페이스에 새로 고침되지 않습니다.
상황 2: JQuery 코드에서 $scope의 데이터를 수정합니다.이 경우angular 프레임워크 밖에서 $scope의 데이터를 조작합니다.angular는 데이터 변화가 정상적임을 감지할 수 없습니다.

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

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.loadListFrom Service () 는 더 이상 제어할 수 없습니다. 로컬에 캐시가 있다면 캐시를 직접 읽는 것이 매우 빠릅니다. 이때 수동 $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의 set Timeout은 angular의 $timeout으로 대체할 수 있습니다. 이것이야말로 $apply를 통해 구제하는 것이 아니라 추천하는 방식입니다.

  
	
    	
	
		
        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"]); 
			
		})
	
  
  
	
{{text}}

그래서 "$apply already in progress"를 해결하는 가장 좋은 방법은 $scope.$를 사용하지 않는 것입니다.apply() 또는 $scope.$digest().
참고 글: prevent error $digest already in progress when calling $scope.$apply()

좋은 웹페이지 즐겨찾기