programing

AngularJS : $scope를 호출할 때 이미 진행 중인 오류 $digest를 방지합니다.$syslog()

minimums 2023. 2. 22. 21:39
반응형

AngularJS : $scope를 호출할 때 이미 진행 중인 오류 $digest를 방지합니다.$syslog()

애플리케이션을 각도로 빌드한 후, 페이지를 수동으로 스코프로 갱신할 필요가 점점 더 많아지고 있습니다.

은 전화하는 입니다.$apply()내 컨트롤러와 지시의 범위 내에서.같은 이다.

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

이 오류를 회피하는 방법이나 다른 방법으로 같은 것을 달성하는 방법을 알고 있는 사람이 있습니까?

최근 Angular guys와의 토론에서 바로 이 주제에 대해:장래를 대비하기 위해서, 다음의 것을 사용하지 말아 주세요.

올바른 방법을 요구했을 때, 그 답은 현재입니다.

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

최근 Facebook, Google, Twitter 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의 delay 인수는 옵션이며 설정되지 않은 상태로 두면 기본값0이 됩니다(지연이 설정되어 있지 않으면 기본값이 0이 됩니다).

조금 직감적이지 않지만, Angular라는 글쓴이의 답변이니까, 난 그걸로 충분해!

이 패턴을 사용하지 마십시오.이 패턴은 해결되는 것보다 더 많은 오류를 일으킵니다.그것이 무엇인가를 고친다고 생각할지라도, 그것은 그렇지 않았다.

하면 '아까보다'가 있는지 할 수 요.$digest 진행중입니다.$scope.$$phase

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

$scope.$$phase"$digest" ★★★★★★★★★★★★★★★★★」"$apply" 그렇다면$digest ★★★★★★★★★★★★★★★★★」$apply행행중중중중다다이 상태들의 차이점은$digest와 그또, 「시계」는, 「시계」의 「시계」가 처리됩니다.」를 처리합니다.$apply는 모든 범위의 감시자를 처리합니다.

@ @dnc253에 를 $digest ★★★★★★★★★★★★★★★★★」$apply★★★★★★★★★★★★★★★★★★★★★★★★★★★일반적으로 DOM 이벤트가 Angular 범위 밖에서 발생한 결과로 스코프 상태를 업데이트해야 할 때 다이제스트해야 합니다.트위터는, 「DOM」의 「DOM」이에 기동하는 경우가 있습니다.$digest진행 중이고, 때로는 그렇지 않을 수도 있습니다.★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

아는 사람이 있다면 더 좋은 방법을 알고 싶어요.


댓글 : by @anddoutoi

angular.js 안티 패턴

  1. 마세요if (!$scope.$$phase) $scope.$apply(), '네'라는 $scope.$apply()콜 스택의 높이가 충분하지 않습니다.

다이제스트 사이클은 동기 콜입니다.완료될 때까지 브라우저의 이벤트 루프를 제어할 수 없습니다.이 문제를 해결하는 몇 가지 방법이 있습니다.이 문제를 해결하는 가장 쉬운 방법은 내장된 $timeout을 사용하는 것입니다.두 번째 방법은 언더스코어 또는 lodash를 사용하는 경우(필요한 경우)에 다음 연락처로 문의하십시오.

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

또는 lodash가 있는 경우:

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

몇 가지 회피책을 시도했지만 모든 컨트롤러, 디렉티브, 심지어 일부 공장에 $rootScope를 투입하는 것은 싫었습니다.지금까지 $timeout과 _.defer가 마음에 들었습니다.이러한 메서드는 다음 애니메이션 루프까지 대기하도록 각도에 지시하여 현재 스코프가 보장되도록 합니다.$1200은 끝났습니다.

이 답변의 대부분은 좋은 조언을 담고 있지만 혼란을 초래할 수도 있습니다. 「」를 사용하는 만으로,$timeout최선의 해결책도 아니고 올바른 해결책도 아닙니다.또, 퍼포먼스나 scalability에 관심이 있는 경우는, 반드시 읽어 주세요.

알아두어야 할 것

  • $$phase이 프레임워크에는 사적인 것이므로 그럴 만한 이유가 있습니다.

  • $timeout(callback) 사이클 경우)이 한 후 한 콜백을 합니다.$apply.

  • $timeout(callback, delay, false)하는) (콜백 실행 전)는하지 않습니다.$apply(세 번째 인수) 각도 모델($scope)을 수정하지 않은 경우 성능이 저장됩니다.

  • $scope.$apply(callback)$rootScope.$digest즉, 고립된 범위 내에 있더라도 애플리케이션과 모든 하위 영역의 루트 범위를 다시 지정합니다.

  • $scope.$digest()는 모델을 뷰와 동기화할 뿐 부모 스코프를 다이제스트 할 수 없기 때문에 (대부분 명령어로부터) HTML의 격리된 부분에서 작업할 때 많은 퍼포먼스를 절약할 수 있습니다.$syslog는 콜백을 받지 않습니다.이치

  • $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 a새 。$evalAsync기본적으로 콜백이 발생하면 현재 다이제스트사이클 내에서 콜백이 실행됩니다.그렇지 않으면 새로운 다이제스트사이클이 콜백 실행을 시작합니다.

은 아직 'A' 않다.$scope.$digest의 것을 있는 HTML 이므로).$apply진행 중인 리소스가 없는 경우 트리거됩니다). 단, 이는 동기 실행 여부를 알 수 없는 함수를 실행하는 경우(예: 캐시될 가능성이 있는 리소스를 가져온 후)에는 최적의 솔루션입니다.이 경우 서버에 대한 비동기 호출이 필요할 수 있습니다.그렇지 않으면 리소스가 동기적으로 로컬로 Import 됩니다.

, 그리고 다른 , 당신이 있던 이 있습니다.!$scope.$$phase , 「 」를 해 주세요.$scope.$evalAsync( callback )

이 프로세스를 건조하게 유지하기 위한 편리한 작은 도우미 방법:

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

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

  }]
)

http://docs.angularjs.org/error/$rootScope:inprog 를 참조해 주세요.

는, 「」에 합니다.$apply($해야 할 Angular 코드 apply'를 ) Angular 코드 내에서 할 수 .$digest already in progress□□□□□□□□★

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

하려면 , 「」를 호출하는 합니다.$apply비동기적으로 실행됩니다. 조작은, 「」에의 콜내에서 할 수 .$timeout이 「」으로 되어 있는 .0을 사용법 「」하면, 「」가 됩니다.$timeout를 호출할 .$apply는 또 다른 $syslog를 때문입니다.$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 코드 밖에서 호출된 콜백 내에서 발생합니다).

「」를 사용하는 것에 , 이 있는 것을 있지 한.$timeout에 걸쳐서$apply할 수 이유를 수 $timeout지연 ) ()$apply이치

이 오류는 기본적으로 보기를 업데이트 중임을 의미합니다. 전화할 는 없어요$apply()제어기 내에 있습니다. 「」를 호출한 이.$apply()모델을 올바르게 업데이트하지 않았을 가능성이 높습니다.구체적인 내용을 올려주시면 핵심 문제를 해결할 수 있을 것 같습니다.

$apply 말합니다

$timeout(angular.noop)

evalAsync를 사용할 수도 있습니다.다이제스트가 끝난 후에 실행됩니다!

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

우선, 이런 식으로 고치지 마세요.

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

이 방법을 사용해도 오류가 발생할 수 있습니다(https://stackoverflow.com/a/12859093/801426)).

이것을 시험해 보세요.

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

컨텍스트에 따라 $evalAsync 또는 $timeout을 사용해야 합니다.

다음 링크에 설명이 잘 되어 있습니다.

http://www.bennadel.com/blog/2605-scope-evalasync-vs-timeout-in-angularjs.htm

써보다

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

대신

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

$applyAsync 나중에 $apply 호출을 스케줄링합니다.이는 동일한 다이제스트에서 평가해야 하는 여러 식을 큐잉하기 위해 사용할 수 있습니다.

메모: $digest 내에서 $적용Async()는 현재 스코프가 $rootScope인 경우에만 플러시됩니다.즉, 자 스코프에서 $digest를 호출해도 $apply가 암묵적으로 플러시되지 않습니다.비동기() 큐

예:

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

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

참고 자료:

1. 범위$applyAsync()와 AngularJS 1.3의 Scope.$evalAsync()

  1. AngularJs 문서

다이제스트 사이클을 트리거하는 것보다 커스텀이벤트를 사용하는 것이 좋습니다.

커스텀 이벤트를 브로드캐스트하고 이 이벤트에 리스너를 등록하는 것은 다이제스트 사이클에 있든 없든 원하는 액션을 트리거하기 위한 좋은 솔루션임을 알게 되었습니다.

커스텀 이벤트를 작성하면 해당 이벤트에 가입된 청취자만 트리거하고 스코프를 호출했을 때와 같이 스코프에 바인드된 모든 워치는 트리거하지 않기 때문에 코드를 보다 효율적으로 사용할 수 있습니다.$140입니다.

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


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

yearofmu는 재사용 가능한 $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);

는 이 하려고 전화했습니다.$eval$apply가 있는 $digest함수가 실행됩니다.

의사들에 따르면$apply기본적으로는 다음과 같습니다.

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

경우에는 ★★★★★★★★★★★★★★★★★★★★★★★★★★★.ng-click 내의 하고, 그의 $"$watch"합니다.$applied. 이에서는 " progress가 발생합니다 이미 진행 중인 작업입니다.

「 」를 치환하는 으로써,$apply$evalwatch 표현식 내에서는 스코프 변수가 예상대로 업데이트됩니다.

따라서 Angular 내의 다른 변경 사항으로 인해 다이제스트가 실행되고 있는 경우,$eval 돼요ing만 하면 요.

$scope.$$phase || $scope.$apply(); 대신에

가 Angular를 하는 것을 한다.$$phase안티패턴을 얻으려고 했는데$timeout ★★★★★★★★★★★★★★★★★」_.defer일하기 위해.

않은 를 만듭니다.{{myVar}}FOUT처럼 돔에 있는 콘텐츠입니다.나에게 이것은 받아들일 수 없었다.뭔가 해킹이라는 말을 독단적으로 들을 필요도 없고, 마땅한 대안이 없다.

매번 효과가 있는 것은 다음과 같습니다.

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

이 방법의 위험성도, 왜 댓글이나 각진 팀에 의한 해킹이라고 하는지 이해할 수 없습니다.이 명령어는 정확하고 읽기 쉬워 보입니다.

"이미 다이제스트를 실행하고 있지 않은 한 다이제스트 실행"

Coffee Script는 더 예뻐요.

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

이거 왜 이래?FOUT를 생성하지 않는 대체 방법이 있습니까? $safeApply는 괜찮아 보이지만,$$phase검사 방법도 있습니다.

이것은 나의 유틸리티 서비스입니다.

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']);
};

저는 이 방법을 사용하고 있는데, 완벽하게 효과가 있는 것 같습니다. 트리거합니다.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);
  }
}

디버거를 무효로 하면, 에러는 발생하지 않습니다.제 경우 디버거가 코드 실행을 중지했기 때문입니다.

위의 답변과 비슷하지만, 이것은 나에게 충실히 작용했다.서비스 추가:

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

이 문제는 기본적으로 digest cycle을 실행하기 위해 angular를 요청하는 경우 발생하며 angular to understanding 문제가 발생하고 콘솔의 결과 예외가 발생합니다.
scope.1을 . 같기 때문에 $ 내에 있는 "syslog 함수"는 "syslog 함수"입니다 함수 내부에는 "syslog 함수"가 하게 동작합니다.$DR()DRADIUSTIONDAREXT CHANGE $RADIUSTION()DRADIUSTION.
2.이하고 있습니다. 않기 때문입니다, Timeout2). 바닐라 자바스크립트「각도로 정의되어 있다」를 선택해, 「타임아웃」을 설정합니다.
하려면 , 3을 사용할 수 .

if(!disclosed)가 되다{$syslog}{
scope}).$evalAsync(함수){

}}; }}

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

이 오류를 피하고 $apply를 회피하기 위한 좋은 솔루션을 소개합니다.

외부 이벤트에 근거해 콜 하는 경우는, 이것을 debounce(0)와 조합할 수 있습니다.위는 사용하고 있는 'debounce'와 코드의 완전한 예시입니다.

.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;
            };
        };
    }
])

그리고 코드 자체에서 이벤트를 듣고 필요한 $120만을 $120으로 호출할 수 있습니다.

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

를 사용하여 오류를 방지할 수 있습니다.

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

발견: https://coderwall.com/p/ngisma에서 Nathan Walker(페이지 하단 부근)는 $rootScope에서 func 'safe Apply'를 만들기 위한 데코레이터를 제안합니다.코드:

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

이것으로 문제가 해결됩니다.

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

언급URL : https://stackoverflow.com/questions/12729122/angularjs-prevent-error-digest-already-in-progress-when-calling-scope-apply

반응형