$ aplicar error ya en progreso

133

Seguimiento de pila:

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

se refiere a este código 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
        });
    })

Lo extraño es que en mi LG4X funciona bien, sin embargo, en mi samsung s2 arroja el error anterior. ¿Alguna idea de lo que está mal?

Voto a favor
fuente
1
¿Has probado stackoverflow.com/a/12859093/1266600 ? Puede ser porque diferentes dispositivos -> diferentes velocidades de procesamiento -> diferentes tiempos, lo que puede causar conflictos en algunos lugares pero no en otros.
sushain97
20
uso$timeout()
Onur Yıldırım
77
+1 al comentario $ timeout (). Ver: stackoverflow.com/questions/12729122/…
Trevor

Respuestas:

106

Recibe este error porque está llamando $applydentro de un ciclo de digestión existente.

La gran pregunta es: ¿por qué llamas $apply? Nunca debería necesitar llamar a $applymenos que esté interactuando desde un evento no angular. La existencia de $applyusualmente significa que estoy haciendo algo mal (a menos que, nuevamente, la aplicación $ suceda debido a un evento no angular).

Si $applyrealmente es apropiado aquí, considere usar un enfoque de "aplicación segura":

https://coderwall.com/p/ngisma

Brian Genisio
fuente
41
El núcleo de la aplicación segura vinculada es un antipatrón (según los documentos) github.com/angular/angular.js/wiki/Anti-Patterns . Si desea una forma de hacerlo compatible con el futuro (¡la fase $$ está desapareciendo!), Envuelva su código en $ timeout () sin tiempo establecido. Se aplicará con seguridad después de que el ciclo de digestión actual se haya completado.
betaorbust
@betaorbust De acuerdo. La aplicación segura es mala. Además, las llamadas que se aplican demasiadas veces pueden causar problemas de rendimiento. Es mejor estructurar el código para evitar el problema todos juntos.
Brian Genisio
no estoy llamando aplicar
circuitería
41

Puedes usar esta declaración:

if ($scope.$root.$$phase != '$apply' && $scope.$root.$$phase != '$digest') {
    $scope.$apply();
}
Dariraze
fuente
1
No se recomienda utilizar variables que comienzan con $$ porque son privadas. En este caso $$ fase
Ara Yeressian
9
Esta respuesta es mucho más útil que la anterior. Necesito una solución, no ser amonestado por algo que pueda estar más allá de mi control. Tenemos una mezcla de código angular y heredado, y tienen que interactuar de alguna manera. Es demasiado caro reescribir todo el código heredado ...
Jordan Lapp
24

Si se debe aplicar el alcance en algunos casos, puede establecer un tiempo de espera para que el $ apply se difiera hasta el siguiente tic

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

o envuelva su código en $ timeout (function () {..}); porque $ aplicará automáticamente el alcance al final de la ejecución. Si necesita que su función se comporte de forma sincrónica, yo haría lo primero.

jeff.d
fuente
Descubrí que necesitaba incluir la acción dentro de setTimeout(function() { $apply(function() {... do stuff ...} ) })@Tamil Vendhan a continuación.
prototipo del
66
No use setTimeout, eso solo crea la necesidad de otro $ apply. Use el marco, tiene un servicio $ timeout que hace todo eso por usted.
Spencer
10

En mi caso, uso $applycon la interfaz de usuario de calendario angular para vincular algún evento:

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

Después de leer el documento del problema: https://docs.angularjs.org/error/ $ rootScope / inprog

La parte API inconsistente (Sync / Async) es muy interesante:

Por ejemplo, imagine una biblioteca de terceros que tiene un método que recuperará datos para nosotros. Como puede estar haciendo una llamada asincrónica a un servidor, acepta una función de devolución de llamada, que se llamará cuando lleguen los datos.

Dado que el constructor MyController siempre se instancia desde una llamada $ apply, nuestro controlador está tratando de ingresar un nuevo bloque $ apply desde dentro de uno.

Cambio el código a:

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

Funciona de maravilla !

Aquí hemos utilizado $ timeout para programar los cambios en el alcance en una futura pila de llamadas. Al proporcionar un período de tiempo de espera de 0 ms, esto ocurrirá lo antes posible y $ timeout garantizará que el código se invoque en un solo bloque $ apply.

mpgn
fuente
1
Su solución $ timeout delay 0 es impresionante.
Ahsan
9

En angular 1.3, creo, agregaron una nueva función - $scope.$applyAsync(). Las llamadas a esta función se aplican más adelante; dicen aproximadamente 10 ms más tarde al menos. No es perfecto, pero al menos elimina el molesto error.

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

usuario3413723
fuente
3

En cualquier momento, solo puede haber una $digestu $applyoperación en progreso. Esto es para evitar que los errores muy difíciles de detectar ingresen a su aplicación. El seguimiento de la pila de este error le permite rastrear el origen de la llamada $applyo ejecución actual $digest, que causó el error.

Más información: https://docs.angularjs.org/error/$rootScope/inprog?p0=$apply

indulgente
fuente
2

Acabo de resolver este problema. Está documentado aquí .

Estaba llamando $rootScope.$applydos veces en el mismo flujo. Todo lo que hice fue envolver el contenido de la función de servicio con un setTimeout(func, 1).


fuente
1

Sé que es una pregunta antigua, pero si realmente necesitas usar $ scope. $ ApplyAsync ();

akaco
fuente
0

Llamo a $ scope. $ Se aplica así a llamadas ignoradas múltiples en una sola vez.

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

simplemente llama

callApply();
Marosdee Uma
fuente