Angular 1.6.0: "Possibly unhandled rejection" 错误

Angular 1.6.0: "Possibly unhandled rejection" error

我们的 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);
        });

下面是我们如何在 Karma 中触发错误:

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

现在,随着更新到 1.6.0,Angular 突然在我们的单元测试(在 Karma 中)抱怨因 "Possibly unhandled rejection" 错误被拒绝的承诺。但是我们正在处理调用错误服务的第二个函数中的拒绝。

Angular 到底在找什么?它要我们如何 "handle" 拒绝?

您显示的代码将处理调用 .then 之前发生的拒绝。在这种情况下,将调用您传递给 .then 的第二个回调,并处理拒绝。

但是,当您调用 .then 的承诺成功时,它会调用第一个回调。 如果此回调抛出异常或 returns 被拒绝的承诺,将不会处理由此产生的拒绝,因为第二次回调不处理第一次引起的拒绝。这就是符合 Promises/A+ 规范的 promise 实现的工作方式,并且 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

通过回滚到 Angular 1.5.9 并重新运行测试发现了问题。这是一个简单的注入问题,但 Angular 1.6.0 通过抛出 "Possibly Unhandled Rejection" 错误取代了这个问题,混淆了实际错误。

我在测试执行期间观察到相同的行为。奇怪的是,生产代码运行良好,仅在测试时失败。

让您的测试满意的简单解决方案是将 catch(angular.noop) 添加到您的 promise 模拟中。对于上面的示例,它应该如下所示:

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

请在此处查看答案:

This has been fixed with 316f60f and the fix is included in the v1.6.1 release.

尝试将此代码添加到您的配置中。我曾经遇到过类似的问题,这个变通办法解决了问题。

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

您可以通过关闭 errorOnUnhandledRejections 来掩盖问题,但错误提示您需要 "handle a possible rejection" 所以您只需要在您的承诺中添加一个 catch。

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

第一个选项只是通过按照建议在 $qProvider 配置中配置 errorOnUnhandledRejections 来隐藏错误并禁用它 Cengkuru Michael

BUT 这只会关闭日志记录。错误本身将保留

在这种情况下,更好的解决方案 将是 - 使用 .catch(fn) 方法处理拒绝:

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

链接:

这可能不是你的具体情况,但我遇到了类似的问题。

在我的例子中,我使用的是 angular-i18n,并异步获取语言环境字典。问题是它得到的 json 文件缩进不正确(混合空格和制表符)。 GET 请求没有失败。

更正缩进解决了问题。

为避免在您的代码中的多个位置键入额外的 .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);
        };
    }
};

我在更新到 Angular 1.6.7 后也遇到了同样的问题,但是当我查看代码时,针对我的案例 $interval.cancel(interval);

抛出了错误

我将 angular-mocks 更新到最新版本 (1.7.0) 后,我的问题得到解决。

我做了一些更改后出现了同样的通知。原来是因为我在单个 $http 请求到使用 angularjs $q 服务的多个请求之间进行了更改。

我没有将它们包装在数组中。例如

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

而不是

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

我希望这可以节省一些时间。