it-swarm.com.ru

Angular 1.6.0: ошибка «возможно необработанное отклонение»

У нас есть шаблон для разрешения обещаний в нашем приложении Angular, который хорошо нам служил вплоть до 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);
        });

И вот как мы вызываем ошибку в Карме:

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

Теперь, с обновлением до 1.6.0, Angular неожиданно жалуется в наших юнит-тестах (в Карме) на отклоненные обещания с ошибкой "Возможно, необработанное отклонение". Но мы обрабатываем отклонение во второй функции, которая вызывает наш сервис ошибок.

Что именно Angular ищет здесь? Как он хочет, чтобы мы "справились" с отказом?

66
Groucho

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

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

Код, который вы показываете, будет обрабатывать отклонение, которое происходит до вызова .then. В такой ситуации будет вызван 2-й обратный вызов, который вы передадите .then, и отклонение будет обработано.

Однако , когда обещание, по которому вы вызываете .then, выполнено успешно, оно вызывает 1-й обратный вызов. Если этот обратный вызов генерирует исключение или возвращает отклоненное обещание, этот результирующий отказ не будет обработан , потому что второй обратный вызов не обрабатывает отклонения, вызванные причиной первый. Именно так работают реализации обещаний, соответствующие спецификации Promises/A + , и обещания Angular соответствуют требованиям.

Вы можете проиллюстрировать это следующим кодом:

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

Если вы запустите его в Node, который также соответствует Promises/A +, вы получите:

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
20
Louis

Обнаружили проблему, откатившись к Angular 1.5.9 и перезапустив тест. Это была простая проблема с внедрением, но Angular 1.6.0 заменила ее, вместо этого выдав ошибку "Возможно, необработанное отклонение", скрывая фактическую ошибку.

18
Groucho

Первый вариант - просто скрыть ошибку, отключив ее, настроив errorOnUnhandledRejections в конфигурации $ qProvider в соответствии с рекомендациями Cengkuru Michael

НО это только отключит ведение журнала. Сама ошибка останется

Лучшее решение в этом случае будет - обработка отказа с помощью метода .catch(fn):

resource.get().$promise
    .then(function (response) {})
    .catch(function (err) {});

ССЫЛКИ:

16
Andrii Verbytskyi

Чтобы избежать необходимости вводить дополнительную .catch(function () {}) в свой код в нескольких местах, вы можете добавить decorator к $exceptionHandler.

Это более подробный вариант, чем другие, но вам нужно внести изменения только в одном месте.

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

Пожалуйста, проверьте ответ здесь:

возможно необработанное отклонение в Angular 1.6

Это было исправлено с помощью 16f60f , и это исправление включено в версия v1.6.1 .

3
Piotr Pradzynski

Вы можете замаскировать проблему, отключив errorOnUnhandledRejected, но ошибка говорит о том, что вам необходимо "обработать возможный отказ", поэтому вам просто нужно добавить уловку к вашему обещанию.

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

Ссылка: https://github.com/angular-ui/ui-router/issues/2889

3
giselleghadyani

Я наблюдал такое же поведение во время выполнения теста. Странно, что на рабочем коде работает нормально и дает сбой только на тестах.

Простое решение сделать ваши тесты счастливыми - это добавить catch(angular.noop) к вашему макету обещания. В случае примера выше это должно выглядеть так:

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

Я также столкнулся с той же проблемой после обновления до Angular 1.6.7, но когда я посмотрел код, в моем случае возникла ошибка для $interval.cancel(interval);

Моя проблема была решена, когда я обновил angular-mocks до последней версии (1.7.0).

2
Dilip

Это может быть не ваша специфическая ситуация, но у меня была похожая проблема.

В моем случае я использовал angular-i18n и получал словарь локали асинхронно. Проблема заключалась в том, что файл json, который он получал, был неправильно с отступом (пробелы и табуляции). Запрос GET не прошел.

Исправление отступа решило проблему.

0
monstercode

У меня было такое же уведомление появиться после внесения некоторых изменений. Это произошло потому, что я переключился с одного запроса $http на несколько запросов с помощью службы angularjs $q.

Я не обернул их в массив. например.

$q.all(request1, request2).then(...) 

скорее, чем

$q.all([request1, request2]).then(...)

Я надеюсь, что это может сэкономить кому-то время.

0
JamesEddyEdwards