AngularJS : $ scope. $ apply ()를 호출 할 때 $ digest 오류가 이미 발생하지 않도록 방지


838

각도로 응용 프로그램을 구축 한 후 페이지를 내 범위로 수동으로 업데이트해야한다는 것을 알게되었습니다.

내가 아는 유일한 방법 $apply()은 내 컨트롤러 및 지시문의 범위에서 호출 하는 것입니다. 이것의 문제는 콘솔에 다음과 같은 오류가 계속 발생한다는 것입니다.

오류 : $ digest가 이미 진행 중입니다

누구 든지이 오류를 피하거나 같은 것을 달성하지만 다른 방식으로 달성하는 방법을 알고 있습니까?


34
점점 더 많은 $ apply를 사용해야한다는 것은 정말 실망스러운 일입니다.
OZ_

콜백에서 $ apply를 호출 하더라도이 오류가 발생합니다. 타사 라이브러리를 사용하여 서버의 데이터에 액세스하고 있으므로 $ http를 이용할 수 없으며 $ http를 사용하기 위해 라이브러리를 다시 작성해야하기 때문에 원하지 않습니다.
Trevor

45
사용$timeout()
Onur Yıldırım

6
$ timeout (fn) + 1을 사용하면 문제를 해결할 수 있습니다.! $ scope. $$ phase는 최상의 솔루션이 아닙니다.
Huei Tan

1
만 코드 / 통화 범위를 포장. $에서 적용 내에서 시간 제한 (안 $ 시간 초과) AJAX 기능 (하지 $ HTTP) 및 이벤트 (하지 ng-*). 당신이 (시간 제한 / 아약스 / 이벤트를 통해 호출되는) 함수 내에서 호출하는 경우 그렇지 않은 것을, 확인 처음로드에서 실행된다.
Patrick

답변:


660

이 패턴을 사용하지 마십시오. 이로 인해 해결되는 것보다 더 많은 오류가 발생합니다. 비록 당신이 무언가를 고쳤다 고 생각했지만 그렇지 않았습니다.

를 확인하여 a $digest가 이미 진행 중인지 확인할 수 있습니다 $scope.$$phase.

if(!$scope.$$phase) {
  //$digest or $apply
}

$scope.$$phase반환 "$digest"또는 "$apply"경우 $digest또는 $apply진행 중입니다. 이 주들 사이의 차이점 $digest은 현재 범위와 그 하위의 시계를 $apply처리하고 모든 범위의 감시자를 처리한다는 것입니다.

@ dnc253의 요점으로, 전화를 $digest하거나 $apply자주 전화를 하면 잘못하고있을 수 있습니다. 나는 일반적으로 Angular의 범위 밖에서 DOM 이벤트가 발생하여 스코프의 상태를 업데이트해야 할 때 요약해야합니다. 예를 들어 트위터 부트 스트랩 모달이 숨겨지는 경우. 때로는 $digest진행 중일 때 DOM 이벤트가 발생 하지만 때로는 그렇지 않습니다. 그래서이 검사를 사용합니다.

누군가 알고 있다면 더 나은 방법을 알고 싶습니다.


의견에서 : @anddoutoi

angular.js 안티 패턴

  1. 하지 마십시오 . 호출 스택에서 충분히 높지 않다는 if (!$scope.$$phase) $scope.$apply()것을 의미합니다 $scope.$apply().

230
$ digest / $ apply처럼 기본적으로이 작업을 수행해야합니다
Roy Truelove

21
경우에 따라 현재 범위와 루트 범위 만 확인해야합니다. 루트에서 $$ phase에 대한 값을 얻었지만 내 범위에는 없습니다. 지시어의 격리 된 범위와 관련이 있다고 생각하지만 ..
Roy Truelove

106
"중지 중지 if (!$scope.$$phase) $scope.$apply()", github.com/angular/angular.js/wiki/Anti-Patterns
anddoutoi

34
@anddoutoi : 합의; 당신은 링크가 이것이 해결책이 아니라는 것을 분명히합니다. 그러나 "콜 스택이 충분하지 않다"는 것이 무엇인지 확실하지 않습니다. 이것이 무엇을 의미하는지 아십니까?
Trevor

13
@ threed : aaronfrost의 답변을 참조하십시오. 올바른 방법은 다음주기에서 지연을 사용하여 요약을 트리거하는 것입니다. 그렇지 않으면 이벤트가 손실되고 범위가 전혀 업데이트되지 않습니다.
Marek

663

이 주제에 대한 Angular 사람들과의 최근 토론에서 : 미래 보장을 위해, 당신은 사용해서는 안됩니다$$phase

"올바른"방법으로 눌렀을 때 답은 현재

$timeout(function() {
  // anything you want can go here and will safely be run on the next digest.
})

나는 최근에 콜백을받은 페이스 북, 구글 및 트위터 API를 포장하기 위해 각도 서비스를 작성할 때이 문제에 부딪쳤다.

다음은 서비스 내에서의 예입니다. (간단하게하기 위해 변수를 설정하고 $ timeout을 주입하는 등의 나머지 서비스는 중단되었습니다.)

window.gapi.client.load('oauth2', 'v2', function() {
    var request = window.gapi.client.oauth2.userinfo.get();
    request.execute(function(response) {
        // This happens outside of angular land, so wrap it in a timeout 
        // with an implied apply and blammo, we're in action.
        $timeout(function() {
            if(typeof(response['error']) !== 'undefined'){
                // If the google api sent us an error, reject the promise.
                deferred.reject(response);
            }else{
                // Resolve the promise with the whole response if ok.
                deferred.resolve(response);
            }
        });
    });
});

$ timeout에 대한 지연 인수는 선택 사항이며 설정되지 않은 경우 기본값은 0입니다 ( $ timeout$ browser.defer 를 호출 하고 지연이 설정되지 않은 경우 기본값은 0 임)

조금 직관적이지 않지만 Angular를 작성하는 사람들의 대답이므로 충분합니다.


5
나는 내 지시 사항 에서이 문제에 여러 번 부딪쳤다. redactor를 위해 하나를 작성하고 있었고 이것은 완벽하게 작동하는 것으로 나타났습니다. 브래드 그린 (Brad Green)과의 만남에서 그는 Angular 2.0이 JS의 고유 한 관찰 능력을 사용하고 다이제스트 사이클이없고 브라우저가없는 브라우저를 위해 폴리 필 (polyfill)을 사용하면 거대 할 것이라고 말했다. 그 시점에서 우리는 더 이상 이것을 할 필요가 없습니다. :)
Michael J. Calkins

어제 $ timeout 내에서 selectize.refreshItems ()를 호출 하면 재귀 다이제스트 오류가 발생 하는 문제 가 있습니다. 그 아이디어가 어떻게 될 수 있습니까?
iwein

3
당신이 사용하는 경우 $timeout오히려 기본보다 setTimeout, 왜 사용하지 않는 $window네이티브 대신 window?
LeeGee

2
@ LeeGee : $timeout이 경우 사용 포인트 $timeout는 각도 범위가 올바르게 업데이트된다는 것입니다. $ digest가 진행 중이 지 않으면 새로운 $ digest가 실행됩니다.
awe

2
@webicy 그건 아냐. $ timeout에 전달 된 함수의 본문이 실행되면 약속이 이미 해결되었습니다! cancel그럴 이유가 없습니다. 로부터 문서 : "이 결과, 약속은 거절로 해결 될 것입니다." 해결 된 약속을 해결할 수 없습니다. 취소해도 오류가 발생하지 않지만 긍정적 인 일은 없습니다.
daemonexmachina

324

다이제스트주기는 동기식 호출입니다. 브라우저 이벤트 루프가 완료 될 때까지 제어하지 않습니다. 이를 처리하는 몇 가지 방법이 있습니다. 이를 처리하는 가장 쉬운 방법은 내장 $ timeout을 사용하는 것이고, 두 번째 방법은 밑줄이나 lodash를 사용하는 경우 다음을 호출하는 것입니다.

$timeout(function(){
    //any code in here will automatically have an apply run afterwards
});

또는 lodash가있는 경우 :

_.defer(function(){$scope.$apply();});

우리는 몇 가지 해결 방법을 시도했으며 모든 컨트롤러, 지시문 및 일부 공장에 $ rootScope를 주입하는 것을 싫어했습니다. 따라서 $ timeout과 _.defer는 지금까지 우리가 가장 좋아했습니다. 이 메소드는 다음 애니메이션 루프까지 기다릴 것을 각도에 성공적으로 지시하여 현재 범위를 적용합니다. $ apply가 끝났습니다.


2
$ timeout (...)을 사용하는 것과 비교할 수 있습니까? 다음 이벤트주기를 연기하기 위해 여러 경우에 $ timeout을 사용했는데 제대로 작동하는 것 같습니다. $ timeout을 사용하지 않는 이유가 있는지 아는 사람이 있습니까?
Trevor

9
이미 사용중인 경우에만 사용해야합니다 underscore.js. 이 솔루션은 defer기능 을 사용하기 위해 전체 밑줄 라이브러리를 가져올 가치가 없습니다 . $timeout모든 사람들이 이미 $timeout다른 라이브러리에 의존하지 않고 각도 를 통해 액세스 할 수 있기 때문에 솔루션을 선호합니다 .
tennisgent

10
사실 ...하지만 밑줄이나 lodash를 사용하지 않는 경우 ...하고있는 일을 재평가해야합니다. 이 두 라이브러리는 코드의 모양을 바 꾸었습니다.
frosty

2
우리는 Restangular에 대한 의존성으로 lodash를 가지고 있습니다 (우리는 곧 ng-route를 위해 Restangular를 제거 할 것입니다). 나는 그것이 좋은 대답이라고 생각하지만 사람들이 밑줄 / 대쉬를 사용한다고 가정하는 것은 좋지 않습니다. 꼭 그 라이브러리는 괜찮습니다. 만약 당신이 그것들을 충분히 사용한다면 ... 요즘 에는 밑줄 포함 시키는 이유의 98 %를 없애는 ES5 방법을 사용 합니다.
BradGreens 2014 년

2
당신은 맞습니다 @SgtPooki. $ timeout을 사용하는 옵션을 포함하도록 답변을 수정했습니다. $ timeout 및 _.defer는 다음 애니메이션 루프까지 대기하여 현재 범위를 적용합니다. $ apply가 종료되었습니다. 정직하게 답변 해 주시고 답변을 업데이트 해 주셔서 감사합니다.
frosty

267

여기에 나오는 많은 답변에는 좋은 조언이 들어 있지만 혼란을 초래할 수도 있습니다. 간단하게 사용이 $timeout입니다 하지 최고의이나 적합한 솔루션. 또한 성능이나 확장 성이 염려되는 경우 반드시 읽어보십시오.

알아야 할 것들

  • $$phase 프레임 워크에 대해 비공개이며 이에 대한 충분한 이유가 있습니다.

  • $timeout(callback)현재 다이제스트 사이클 (있는 경우)이 완료 될 때까지 기다린 다음 콜백을 실행 한 다음 끝까지 실행합니다 $apply.

  • $timeout(callback, delay, false)콜백을 실행하기 전에 선택적 지연과 함께 동일한 작업을 수행하지만 $applyAngular 모델 ($ scope)을 수정하지 않은 경우 성능을 저장 하는 (세 번째 인수)를 발생시키지 않습니다.

  • $scope.$apply(callback)는 무엇보다도 호출합니다. $rootScope.$digest즉, 격리 된 범위 내에 있더라도 응용 프로그램 및 모든 해당 자식의 루트 범위를 다시 정의합니다.

  • $scope.$digest()단순히 모델을 뷰에 동기화하지만 부모 범위를 소화하지 않으므로 HTML의 고립 된 부분을 고립 된 범위 (주로 지시문)로 작업 할 때 많은 성능을 저장할 수 있습니다. $ digest는 콜백을받지 않습니다 : 코드를 실행 한 다음 요약하십시오.

  • $scope.$evalAsync(callback)angularjs 1.2와 함께 도입되었으며 아마도 대부분의 문제를 해결할 것입니다. 이에 대한 자세한 내용은 마지막 단락을 참조하십시오.

  • 을 얻는다면 $digest already in progress error아키텍처가 잘못되었습니다. 스코프를 다시 지정할 필요가 없거나 담당하지 않아야합니다. (아래 참조).

코드를 구성하는 방법

해당 오류가 발생하면 스코프가 이미 진행중인 동안 스코프를 다이제스트하려고합니다.이 시점에서 스코프의 상태를 알 수 없으므로 해당 다이제스트를 처리 할 책임이 없습니다.

function editModel() {
  $scope.someVar = someVal;
  /* Do not apply your scope here since we don't know if that
     function is called synchronously from Angular or from an
     asynchronous code */
}

// Processed by Angular, for instance called by a ng-click directive
$scope.applyModelSynchronously = function() {
  // No need to digest
  editModel();
}

// Any kind of asynchronous code, for instance a server request
callServer(function() {
  /* That code is not watched nor digested by Angular, thus we
     can safely $apply it */
  $scope.$apply(editModel);
});

그리고 큰 Angular 응용 프로그램의 일부인 동안 격리 된 작은 지시문에서 수행중인 작업을 알고 있다면 성능을 절약하기 위해 $ apply 대신 $ digest를 선호 할 수 있습니다.

Angularjs 1.2 이후 업데이트

$ scope에 새롭고 강력한 방법이 추가되었습니다. $evalAsync . 기본적으로 현재 다이제스트 사이클이 발생하면 콜백을 실행합니다. 그렇지 않으면 새 다이제스트 사이클이 콜백을 실행하기 시작합니다.

$scope.$digestHTML의 분리 된 부분 만 동기화해야한다는 것을 실제로 알고 있다면 여전히 좋지는 않지만 ( $apply아무 것도 진행되지 않으면 새로운 것이 트리거 되기 때문에 ) 이것은 함수를 실행할 때 가장 좋은 솔루션입니다 어떤 동기 여부를 실행한다면 당신은 그것을 알 수 없다 때때로이 서버에 비동기 호출이 필요합니다 그렇지 않으면 자원이 동 기적으로 로컬로 가져온 것입니다 : 잠재적으로 캐시 된 자원을 가져 오는 한 후 예를 들어,.

이러한 경우 및 귀하가있는 다른 모든 경우, !$scope.$$phase사용하십시오$scope.$evalAsync( callback )


4
$timeout지나가는 것에 비판을받습니다. 피해야 할 더 많은 이유를 줄 수 있습니까 $timeout?
mlhDev

88

이 프로세스를 유지하는 편리한 작은 도우미 방법

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

6
귀하의 safeApply는 무엇보다 많은 일이 일어나고 있음을 이해하도록 도와주었습니다. 게시 해 주셔서 감사합니다.
Jason More

4
나는 똑같은 일을하려고했지만 이것이하지 않으면 fn ()의 변경 사항이 $ digest에 의해 보이지 않을 가능성이 있다는 것을 의미합니까? scope. $$ phase === '$ digest'가정하면 함수를 지연시키는 것이 더 좋지 않습니까?
스펜서 알제

때로는 $ apply ()가 다이제스트를 트리거하는 데 사용되며 fn 만 호출하면 문제가되지 않습니다.
CMCDragonkai

1
fn ()이 fn이 아닌 함수를 실행하기 때문에 scope.$apply(fn);있어야한다고 생각 합니다 scope.$apply(fn());. 내가 틀린 곳으로 도와주세요
madhu131313

1
@ZenOut $ apply 호출은 함수를 포함하여 다양한 종류의 인수를 지원합니다. 함수를 전달하면 함수를 평가합니다.
boxmein

33

예를 들어 CodeMirror와 같은 타사 스크립트와 Krpano와 같은 문제가 있었고 여기에 언급 된 safeApply 메소드를 사용해도 오류가 해결되지 않았습니다.

그러나 해결 한 것은 $ timeout 서비스를 사용하는 것입니다 (먼저 주입하는 것을 잊지 마십시오).

따라서 다음과 같은 것이 있습니다.

$timeout(function() {
  // run my code safely here
})

코드 내부에서 사용중인 경우

아마도 팩토리 지시자의 컨트롤러 안에 있거나 바인딩이 필요하기 때문에 다음과 같이 할 것입니다.

.factory('myClass', [
  '$timeout',
  function($timeout) {

    var myClass = function() {};

    myClass.prototype.surprise = function() {
      // Do something suprising! :D
    };

    myClass.prototype.beAmazing = function() {
      // Here 'this' referes to the current instance of myClass

      $timeout(angular.bind(this, function() {
          // Run my code safely here and this is not undefined but
          // the same as outside of this anonymous function
          this.surprise();
       }));
    }

    return new myClass();

  }]
)

32

http://docs.angularjs.org/error/$rootScope:inprog를 참조 하십시오

문제는 호출 $apply이 때때로 Angular 코드 외부에서 비동기 적으로 실행되고 ($ apply를 사용해야 할 때) 때로는 Angular 코드 내에서 동기식으로 실행될 때 발생합니다 ($digest already in progress 오류 경우에 발생합니다.

예를 들어 서버에서 항목을 비동기식으로 가져 와서 캐시하는 라이브러리가있는 경우 이런 상황이 발생할 수 있습니다. 항목이 처음 요청되면 코드 실행을 차단하지 않도록 비동기식으로 검색됩니다. 그러나 두 번째로 항목은 이미 캐시에 있으므로 동 기적으로 검색 할 수 있습니다.

이 오류를 방지하는 방법은 호출하는 코드가 $apply비동기 적으로 실행되도록하는 것입니다. $timeout지연 시간을 0기본값으로 설정하여 호출 내부에서 코드를 실행하면 됩니다 . 그러나 $ timeout은 자체적으로 다른 주기를 트리거 하여 필요한 모든 업데이트 등을 수행 하기 때문에 내부에서 코드 $timeout를 호출 $apply하면 을 호출 할 필요가 없습니다 $digest.

해결책

간단히 말해서, 이것을하는 대신 :

... your controller code...

$http.get('some/url', function(data){
    $scope.$apply(function(){
        $scope.mydate = data.mydata;
    });
});

... more of your controller code...

이 작업을 수행:

... your controller code...

$http.get('some/url', function(data){
    $timeout(function(){
        $scope.mydate = data.mydata;
    });
});

... more of your controller code...

전화 만 $apply실행중인 코드를 알고있는 경우 하면 항상 Angular 코드 외부에서 실행됩니다 (예 : $ apply 호출은 Angular 코드 외부의 코드에 의해 호출되는 콜백 내에서 발생 함).

누군가 $timeoutover 사용에 대한 몇 가지 강력한 단점을 알고 $apply있지 않으면 거의 같은 일을 하기 때문에 왜 $timeout대신 대신 (제로 지연으로) 사용할 수 없었는지 알 수 없습니다 $apply.


고마워, 이것은 내가 전화하지 $apply않고 여전히 오류가 발생하는 경우에 효과적이었습니다.
ariscris

5
가장 큰 차이점은 즉 $apply하면서 (다음 코드를 적용 $ 다음 콜백이 실행됩니다) 동기 $timeout제한 시간이 현재의 코드가 실행 한 후 새로운 스택은 당신이 사용하는 것처럼, 콜백로 시작하지 않습니다 setTimeout. 동일한 모델을 두 번 업데이트하는 경우 그래픽 결함이 발생할 수 있습니다 $timeout. 다시 업데이트하기 전에 뷰가 새로 고쳐질 때까지 기다리십시오.
floribon 2016 년

정말로 감사합니다. $ watch 활동의 결과로 호출 된 메소드가 있었고 외부 필터가 실행되기 전에 UI를 업데이트하려고했습니다. $ timeout 함수 안에 넣는 것이 나를 위해 일했습니다.
djmarquette

28

이 오류가 발생하면 기본적으로 이미 뷰를 업데이트하는 중임을 의미합니다. $apply()컨트롤러 내 에서 전화 할 필요는 없습니다 . 뷰가 예상대로 업데이트되지 않고를 호출 한 후이 오류가 발생 $apply()하면 모델을 올바르게 업데이트하지 않았 음을 의미합니다. 구체적인 내용을 게시하면 핵심 문제를 파악할 수 있습니다.


그는 AngularJS가 바인딩을 "매직 적으로"볼 수 없다는 것을 알기 위해 하루 종일 보냈고 때로는 $ apply ()로 그를 밀어야합니다.
OZ_

무엇을 의미 you're not updating the the model correctly합니까? $scope.err_message = 'err message';올바른 업데이트가 아닙니까?
OZ_

2
호출해야 할 유일한 것은 $apply()각도의 모델 "외부"(예 : jQuery 플러그인에서)를 업데이트 할 때입니다. 보기가 옳지 않게 보이는 함정에 빠지기 쉽기 때문에 $apply()어디서나 많은 것을 던지면 OP에서 볼 수있는 오류가 발생합니다. 내가 말할 때 you're not updating the the model correctly모든 비즈니스 로직이 범위에있을 수있는 것을 올바르게 채우지 못한다는 것을 의미하므로보기가 예상대로 보이지 않습니다.
dnc253

@ dnc253 나는 동의하고 답을 썼다. 내가 지금 알고있는 것을 알면 $ timeout (function () {...}); _.defer와 동일한 기능을 수행합니다. 둘 다 다음 애니메이션 루프를 연기합니다.
frosty

14

가장 안전한 형태 $apply는 다음과 같습니다.

$timeout(angular.noop)

11

evalAsync를 사용할 수도 있습니다. 다이제스트가 완료된 후 언젠가 실행됩니다!

scope.evalAsync(function(scope){
    //use the scope...
});

10

우선,이 방법으로 고치지 마십시오

if ( ! $scope.$$phase) { 
  $scope.$apply(); 
}

$ phase는 $ digest주기의 부울 플래그이므로 $ apply ()가 실행되지 않기 때문에 의미가 없습니다. 그리고 그것은 나쁜 습관임을 기억하십시오.

대신에 $timeout

    $timeout(function(){ 
  // Any code in here will automatically have an $scope.apply() run afterwards 
$scope.myvar = newValue; 
  // And it just works! 
});

밑줄이나 lodash를 사용하는 경우 defer ()를 사용할 수 있습니다.

_.defer(function(){ 
  $scope.$apply(); 
});

9

이 방법을 사용하면 때때로 오류가 계속 발생합니다 ( https://stackoverflow.com/a/12859093/801426 ).

이 시도:

if(! $rootScope.$root.$$phase) {
...

5
! $ scope. $$ phase와! $ scope. $ root. $$ phase (! $ rootScope. $ root. $$ phase 아님)를 모두 사용하면 효과가 있습니다. +1
asprotte

2
$rootScopeanyScope.$root같은 사람입니다. $rootScope.$root중복됩니다.
floribon


5

사용해보십시오

$scope.applyAsync(function() {
    // your code
});

대신에

if(!$scope.$$phase) {
  //$digest or $apply
}

$ applyAsync $ apply의 호출이 나중에 발생하도록 예약합니다. 동일한 다이제스트에서 평가해야하는 여러 식을 대기열에 넣는 데 사용할 수 있습니다.

참고 : $ digest 내에서 $ applyAsync ()는 현재 범위가 $ rootScope 인 경우에만 플러시됩니다. 이는 하위 범위에서 $ digest를 호출하면 $ applyAsync () 큐를 암시 적으로 플러시하지 않습니다.

예 :

  $scope.$applyAsync(function () {
                if (!authService.authenticated) {
                    return;
                }

                if (vm.file !== null) {
                    loadService.setState(SignWizardStates.SIGN);
                } else {
                    loadService.setState(SignWizardStates.UPLOAD_FILE);
                }
            });

참고 문헌 :

1. AngularJS 1.3의 Scope. $ applyAsync () vs. Scope. $ evalAsync ()

  1. AngularJs 문서

4

다이제스트 사이클을 트리거하는 대신 사용자 지정 이벤트를 사용하는 것이 좋습니다.

사용자 지정 이벤트를 브로드 캐스팅하고이 이벤트에 리스너를 등록하는 것이 다이제스트주기에 있는지 여부에 관계없이 발생하는 작업을 트리거하는 좋은 솔루션이라는 것을 알게되었습니다.

사용자 지정 이벤트를 만들면 해당 이벤트에 가입 한 리스너 만 트리거하고 scope. $ apply를 호출했을 때처럼 스코프에 바인딩 된 모든 시계를 트리거하지 않기 때문에 코드를 더욱 효율적으로 사용할 수 있습니다.

$scope.$on('customEventName', function (optionalCustomEventArguments) {
   //TODO: Respond to event
});


$scope.$broadcast('customEventName', optionalCustomEventArguments);

3

yearofmoo는 재사용 가능한 $ safeApply 함수를 만드는 데 큰 역할을했습니다.

https://github.com/yearofmoo/AngularJS-Scope.SafeApply

사용법 :

//use by itself
$scope.$safeApply();

//tell it which scope to update
$scope.$safeApply($scope);
$scope.$safeApply($anotherScope);

//pass in an update function that gets called when the digest is going on...
$scope.$safeApply(function() {

});

//pass in both a scope and a function
$scope.$safeApply($anotherScope,function() {

});

//call it on the rootScope
$rootScope.$safeApply();
$rootScope.$safeApply($rootScope);
$rootScope.$safeApply($scope);
$rootScope.$safeApply($scope, fn);
$rootScope.$safeApply(fn);

2

함수가 실행 되는 곳을 $eval대신 호출하는 대신 이 문제를 해결할 수있었습니다 .$apply$digest

에 따르면 문서 , $apply기본적으로이 수행합니다

function $apply(expr) {
  try {
    return $eval(expr);
  } catch (e) {
    $exceptionHandler(e);
  } finally {
    $root.$digest();
  }
}

필자의 경우 ng-click범위 내에서 변수를 변경하고 해당 변수의 $ watch는 다른 변수를 변경해야합니다 $applied. 이 마지막 단계는 "이미 다이제스트 진행 중"오류를 발생시킵니다.

대체함으로써 $apply$eval시계 발현 내부의 범위 변수는 예상대로 업데이트됩니다.

따라서, 표시 다이제스트 어쨌든 때문에 각도 내에서 다른 변화의 실행하려고하면 것을,$eval 'ING 당신이해야 할 것입니다.



1

각 문서가 체크를 호출하는 것이 이해 안티 패턴을 , 내가 가려고 하고$$phase$timeout_.defer 일에.

시간 초과 및 지연된 메소드 {{myVar}}FOUT 과 같이 DOM에서 구문 분석되지 않은 컨텐츠 의 플래시를 작성합니다. . 나에게 이것은 받아 들일 수 없었다. 그것은 어떤 것이 해킹이며 적절한 대안이 없다는 것을 교리 적으로 말하지 않아도됩니다.

매번 작동하는 유일한 것은 :

if(scope.$$phase !== '$digest'){ scope.$digest() }.

나는이 방법의 위험을 이해하지 못하며, 왜 의견과 각 팀의 사람들이 해킹으로 묘사했는지 이해하지 못합니다. 명령은 정확하고 읽기 쉬운 것 같습니다.

"이미 발생하지 않는 한 다이제스트 수행"

CoffeeScript에서는 훨씬 더 예쁘습니다.

scope.$digest() unless scope.$$phase is '$digest'

이것에 무슨 문제가 있습니까? FOUT을 만들지 않는 대안이 있습니까? $ safeApply 는 잘 보이지만 $$phase검사 방법도 사용합니다 .


1
이 질문에 대한 정보에 대한 답변을보고 싶습니다!
벤 휠러

그것은 당신이 컨텍스트를 놓치거나 코드를 이해하지 못한다는 것을 의미하기 때문에 해킹입니다. 코드의 그 시점에서 그것을 알 수 없다면, 당신은 그것을 소화 할 책임이 없습니다
floribon

1

이것은 내 utils 서비스입니다.

angular.module('myApp', []).service('Utils', function Utils($timeout) {
    var Super = this;

    this.doWhenReady = function(scope, callback, args) {
        if(!scope.$$phase) {
            if (args instanceof Array)
                callback.apply(scope, Array.prototype.slice.call(args))
            else
                callback();
        }
        else {
            $timeout(function() {
                Super.doWhenReady(scope, callback, args);
            }, 250);
        }
    };
});

그리고 이것은 사용법의 예입니다.

angular.module('myApp').controller('MyCtrl', function ($scope, Utils) {
    $scope.foo = function() {
        // some code here . . .
    };

    Utils.doWhenReady($scope, $scope.foo);

    $scope.fooWithParams = function(p1, p2) {
        // some code here . . .
    };

    Utils.doWhenReady($scope, $scope.fooWithParams, ['value1', 'value2']);
};

1

나는이 방법을 사용하고 있으며 완벽하게 작동하는 것 같습니다. 이것은 사이클이 완료된 시간 동안 기다린 다음 트리거됩니다 apply(). apply(<your scope>)원하는 곳 어디에서나 함수 를 호출하면됩니다 .

function apply(scope) {
  if (!scope.$$phase && !scope.$root.$$phase) {
    scope.$apply();
    console.log("Scope Apply Done !!");
  } 
  else {
    console.log("Scheduling Apply after 200ms digest cycle already in progress");
    setTimeout(function() {
        apply(scope)
    }, 200);
  }
}

1

디버거를 비활성화하면 더 이상 오류가 발생하지 않습니다. 제 경우 에는 디버거가 코드 실행을 중지했기 때문입니다.


0

위의 답변과 비슷하지만 이것은 나를 위해 충실하게 작동했습니다 ... 서비스 추가 :

    //sometimes you need to refresh scope, use this to prevent conflict
    this.applyAsNeeded = function (scope) {
        if (!scope.$$phase) {
            scope.$apply();
        }
    };

0

당신이 사용할 수있는

$timeout

오류를 방지하기 위해.

 $timeout(function () {
                        var scope = angular.element($("#myController")).scope();
                        scope.myMethod();
                        scope.$scope();
                    },1);

나는 $ 제한 시간 사용하지 않으려면 어떻게해야하나요
rahim.nagori

0

문제는 기본적으로 다이제스트 사이클을 실행하기 위해 각도를 요구할 때 발생합니다. 콘솔에서 결과 예외.
1. $ timeout 함수 내에서 scope. $ apply ()를 호출하는 것은 의미가 없습니다.
2. 코드는 기본 각도가 정의되지 않았기 때문에 바닐라 JavaScript 함수와 함께 사용됩니다. 즉 setTimeout
3.이를 위해

if (! scope. $$ phase) {
scope. $ evalAsync (function () {

}); }


0
        let $timeoutPromise = null;
        $timeout.cancel($timeoutPromise);
        $timeoutPromise = $timeout(() => {
            $scope.$digest();
        }, 0, false);

여기에 좋은 해결책이 있습니다. 이 오류를 피하고 $ apply 피하는 이 있습니다.

외부 이벤트를 기반으로 호출하는 경우이를 debounce (0)과 결합 할 수 있습니다. 위는 우리가 사용하는 '디 바운스'와 코드의 전체 예제입니다

.factory('debounce', [
    '$timeout',
    function ($timeout) {

        return function (func, wait, apply) {
            // apply default is true for $timeout
            if (apply !== false) {
                apply = true;
            }

            var promise;
            return function () {
                var cntx = this,
                    args = arguments;
                $timeout.cancel(promise);
                promise = $timeout(function () {
                    return func.apply(cntx, args);
                }, wait, apply);
                return promise;
            };
        };
    }
])

이벤트 자체를 듣고 필요한 $ scope에서만 $ digest를 호출 하는 코드 자체

        let $timeoutPromise = null;
        let $update = debounce(function () {
            $timeout.cancel($timeoutPromise);
            $timeoutPromise = $timeout(() => {
                $scope.$digest();
            }, 0, false);
        }, 0, false);

        let $unwatchModelChanges = $scope.$root.$on('updatePropertiesInspector', function () {
            $update();
        });


        $scope.$on('$destroy', () => {
            $timeout.cancel($update);
            $timeout.cancel($timeoutPromise);
            $unwatchModelChanges();
        });

-3

이것을 발견하십시오 : https://coderwall.com/p/ngisma 여기서 Nathan Walker (페이지 하단 근처)는 func 'safeApply'를 생성하기 위해 $ rootScope의 데코레이터를 제안합니다.

yourAwesomeModule.config([
  '$provide', function($provide) {
    return $provide.decorator('$rootScope', [
      '$delegate', function($delegate) {
        $delegate.safeApply = function(fn) {
          var phase = $delegate.$$phase;
          if (phase === "$apply" || phase === "$digest") {
            if (fn && typeof fn === 'function') {
              fn();
            }
          } else {
            $delegate.$apply(fn);
          }
        };
        return $delegate;
      }
    ]);
  }
]);

-7

이것은 당신의 문제를 해결할 것입니다 :

if(!$scope.$$phase) {
  //TODO
}

(! $ scope. $$ phase) $ scope. $ apply () 인 경우, 호출 스택에서 $ scope. $ apply ()가 충분히 높지 않다는 것을 의미합니다.
MGot90 2016 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.