$ применить уже в процессе ошибки

133

Трассировки стека:

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 он работает нормально, но на моем samsung s2 выдает вышеуказанную ошибку. Есть идеи, что не так?

Upvote
источник
1
Вы пробовали stackoverflow.com/a/12859093/1266600 ? Это может быть связано с тем, что разные устройства -> разные скорости обработки -> разные тайминги, что может вызывать конфликты в одних местах, но не в других.
sushain97 05
20
use$timeout()
Онур Йылдырым
7
+1 к комментарию $ timeout (). См .: stackoverflow.com/questions/12729122/…
Trevor

Ответы:

106

Вы получаете эту ошибку, потому что вызываете $applyвнутри существующего цикла пищеварения.

Большой вопрос: зачем ты звонишь $apply? Вам никогда не нужно звонить, $applyесли вы не взаимодействуете с не-Angular событием. Существование $applyобычно означает, что я делаю что-то не так (если, опять же, $ apply не происходит из не-Angular события).

Если это $applyдействительно уместно, рассмотрите возможность использования подхода «безопасного применения»:

https://coderwall.com/p/ngisma

Брайан Дженисио
источник
41
Ядром связанного безопасного приложения является антипаттерн (согласно документации) github.com/angular/angular.js/wiki/Anti-Patterns . Если вам нужен поддерживаемый в будущем ($$ этап уходит!) Способ сделать это, оберните свой код в $ timeout () без установленного времени. Он будет безопасно применяться после завершения текущего цикла дайджеста.
betaorbust
@betaorbust Согласен. Безопасное применение - это плохо. Кроме того, слишком частый вызов apply может вызвать проблемы с производительностью. Лучше всего структурировать код, чтобы полностью избежать проблемы.
Брайан Генизио
Я не звоню, применяю
схема
41

Вы можете использовать это утверждение:

if ($scope.$root.$$phase != '$apply' && $scope.$root.$$phase != '$digest') {
    $scope.$apply();
}
Dariraze
источник
1
Не рекомендуется использовать переменные, начинающиеся с $$, потому что они частные. В данном случае фаза $$
Ара Ерессян
9
Этот ответ намного полезнее, чем приведенный выше. Мне нужно решение, чтобы меня не упрекали за то, что может быть вне моего контроля. У нас есть смесь углового и унаследованного кода, и они должны как-то взаимодействовать. Просто переписать весь устаревший код слишком дорого ...
Джордан Лапп
24

Если в некоторых случаях должна применяться область видимости, вы можете установить тайм-аут, чтобы $ apply откладывалось до следующего тика.

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

или заключите свой код в $ timeout (function () {..}); потому что он автоматически $ применит область видимости в конце выполнения. Если вам нужно, чтобы ваша функция работала синхронно, я сделаю первое.

jeff.d
источник
Я обнаружил, что мне нужно включить действие в setTimeout(function() { $apply(function() {... do stuff ...} ) })@Tamil Vendhan ниже.
прототип
6
Не используйте setTimeout, это просто создает потребность в другом $ apply. Используйте фреймворк, у него есть сервис $ timeout, который все это делает за вас.
Спенсер
10

В моем случае я использую $applyинтерфейс календаря angular для привязки некоторого события:

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

После прочтения документа о проблеме: https://docs.angularjs.org/error/ $ rootScope / inprog

Часть несогласованного API (Sync / Async) очень интересна:

Например, представьте стороннюю библиотеку, у которой есть метод, который будет получать данные для нас. Поскольку он может выполнять асинхронный вызов на сервер, он принимает функцию обратного вызова, которая будет вызываться при поступлении данных.

Поскольку конструктор MyController всегда создается из вызова $ apply, наш обработчик пытается ввести новый блок $ apply из него.

Я меняю код на:

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

Работает как шарм!

Здесь мы использовали $ timeout, чтобы запланировать изменения области в будущем стеке вызовов. Если задать период ожидания 0 мс, это произойдет как можно скорее, а $ timeout гарантирует, что код будет вызываться в одном блоке $ apply.

mpgn
источник
1
Ваше решение $ timeout delay 0 потрясающее.
Ahsan
9

В angular 1.3, думаю, добавили новую функцию - $scope.$applyAsync(). Вызов этой функции применяется позже - говорят, по крайней мере, через 10 мс. Это не идеально, но, по крайней мере, устраняет досадную ошибку.

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

user3413723
источник
3

В любой момент времени может быть только одна операция $digestили $apply. Это сделано для того, чтобы очень трудно обнаружить ошибки, попадающие в ваше приложение. Трассировка стека этой ошибки позволяет отследить происхождение текущего исполняемого $applyили$digest вызова вызова, вызвавшего ошибку.

Подробнее: https://docs.angularjs.org/error/$rootScope/inprog?p0=$apply

forgottofly
источник
2

Только что решил эту проблему. Это задокументировано здесь .

Я звонил $rootScope.$applyдважды в одном потоке. Все, что я сделал, это обернул содержимое служебной функции расширением setTimeout(func, 1).


источник
1

Я знаю, что это старый вопрос, но если вам действительно нужно использовать $ scope. $ ApplyAsync ();

akaco
источник
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();
Маросди Ума
источник