javascript - Angular 1.6.0: error de "rechazo posiblemente no controlado"




angularjs karma-runner (8)

Esta pregunta ya tiene una respuesta aquí:

Tenemos un patrón para resolver promesas en nuestra aplicación Angular que nos ha servido hasta Angular 1.6.0:

    resource.get().$promise
        .then(function (response) {
        // do something with the response
        }, function (error) {
            // pass the error the the error service
            return errorService.handleError(error);
        });

Y así es como estamos desencadenando el error en Karma:

    resourceMock.get = function () {
        var deferred = $q.defer();
        deferred.reject(error);
        return { $promise: deferred.promise };
    };

Ahora, con la actualización a 1.6.0, Angular se queja repentinamente en nuestras pruebas unitarias (en Karma) por promesas rechazadas con un error de "rechazo posiblemente no controlado". Pero estamos manejando el rechazo en la segunda función que llama a nuestro servicio de errores.

¿Qué está buscando exactamente Angular aquí? ¿Cómo quiere que "manejemos" el rechazo?


El código que muestres manejará un rechazo que ocurre antes de la llamada a .then . En tal situación, se .then a la segunda devolución de llamada que pase a .then , y se manejará el rechazo.

Sin embargo , cuando la promesa a la que llama .then es exitosa, llama a la primera devolución de llamada. Si esta devolución de llamada arroja una excepción o devuelve una promesa rechazada, este rechazo resultante no será manejado , porque la segunda devolución de llamada no maneja los rechazos causados por la primera. Así es como funcionan las implementaciones prometedoras que cumplen con la especificación Promises/A+ , y las promesas angulares son compatibles.

Puede ilustrar esto con el siguiente código:

function handle(p) {
    p.then(
        () => {
            // This is never caught.
            throw new Error("bar");
        },
        (err) => {
            console.log("rejected with", err);
        });
}

handle(Promise.resolve(1));
// We do catch this rejection.
handle(Promise.reject(new Error("foo")));

Si lo ejecuta en Node, que también se ajusta a Promises / A +, obtendrá:

rejected with Error: foo
    at Object.<anonymous> (/tmp/t10/test.js:12:23)
    at Module._compile (module.js:570:32)
    at Object.Module._extensions..js (module.js:579:10)
    at Module.load (module.js:487:32)
    at tryModuleLoad (module.js:446:12)
    at Function.Module._load (module.js:438:3)
    at Module.runMain (module.js:604:10)
    at run (bootstrap_node.js:394:7)
    at startup (bootstrap_node.js:149:9)
    at bootstrap_node.js:509:3
(node:17426) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 2): Error: bar

Encontré el problema volviendo a Angular 1.5.9 y volviendo a ejecutar la prueba. Fue un problema de inyección simple, pero Angular 1.6.0 reemplazó esto al arrojar el error "Rechazo posiblemente no controlado" en su lugar, ofuscando el error real.


He observado el mismo comportamiento durante la ejecución de la prueba. Es extraño que en el código de producción funcione bien y falle solo en las pruebas.

Una solución fácil para hacer felices sus pruebas es agregar catch(angular.noop) a su simulacro de promesa. En el caso del ejemplo anterior, debería verse así:

resourceMock.get = function () {
    var deferred = $q.defer();
    deferred.reject(error);
    return { $promise: deferred.promise.catch(angular.noop) };
};


Intente agregar este código a su configuración. Tuve un problema similar una vez, y esta solución hizo el truco.

app.config(['$qProvider', function ($qProvider) {
    $qProvider.errorOnUnhandledRejections(false);
}]);

Para evitar tener que escribir .catch(function () {}) adicional en su código en varios lugares, puede agregar un decorator al $exceptionHandler .

Esta es una opción más detallada que las otras, pero solo tiene que hacer el cambio en un lugar.

angular
    .module('app')
    .config(configDecorators);

configDecorators.$inject = ["$provide"];
function configDecorators($provide) {

    $provide.decorator("$exceptionHandler", exceptionHandler);

    exceptionHandler.$inject = ['$delegate', '$injector'];
    function exceptionHandler($delegate, $injector) {
        return function (exception, cause) {

            if ((exception.toString().toLowerCase()).includes("Possibly unhandled rejection".toLowerCase())) {
                console.log(exception); /* optional to log the "Possibly unhandled rejection" */
                return;
            }
            $delegate(exception, cause);
        };
    }
};


Puede que no sea su situación específica, pero tuve un problema similar.

En mi caso, estaba usando angular-i18n y obtenía el diccionario local de forma asincrónica. El problema era que el archivo json que estaba recibiendo tenía una sangría incorrecta (mezcla de espacios y pestañas). La solicitud GET no falló.

Corregir la sangría resolvió el problema.


También estaba enfrentando el mismo problema después de actualizar a Angular 1.6.7, pero cuando busqué en el código, se produjo un error para $interval.cancel(interval); para mi caso

Mi problema se resolvió una vez que actualicé los angular-mocks a la última versión (1.7.0).





angularjs-1.6