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

У нас есть шаблон для разрешения promises в нашем 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 неожиданно жалуется на наши модульные тесты (в Карме) для отклонения promises с ошибкой "Возможно необработанное отклонение". Но мы обрабатываем отказ во второй функции, которая вызывает нашу службу ошибок.

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

Ответ 1

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

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

Ответ 2

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

Ответ 3

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

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

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

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

Ответ 4

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

НО, это приведет к отключению регистрации. Сама ошибка останется

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

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

LINKS:

Ответ 6

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

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

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

Ответ 7

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

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

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