角度1.6.0: “可能未处理拒绝”错误

我们有一个模式来解决我们的角度应用程序的承诺,已经为我们提供了良好的直到角度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 突然在我们的单元测试中(在 Karma 中)抱怨由于“可能未经处理的拒绝”错误而被拒绝的承诺。但是我们在调用错误服务的第二个函数中处理拒绝。

Angular 到底在这里寻找什么? 它希望我们如何“处理”拒绝?

127521 次浏览

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

但是 ,当您调用 .then的承诺成功时,它将调用第一个回调。因为第二个回调函数不处理第一个回调函数引起的拒绝。这就是符合 承诺/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 中运行它,它也符合誓言/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通过抛出“可能未处理的拒绝”错误取代了这个问题,混淆了实际的错误。

我在测试执行期间观察到了相同的行为。奇怪的是,在生产环境中,代码运行良好,只有在测试中才会失败。

使您的测试满意的简单解决方案是将 catch(angular.noop)添加到您的承诺模拟中。在上面的例子中,它应该是这样的:

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

请点击这里查看答案:

角度1.6可能有未处理的排斥反应

这个问题已经在 316f60f中得到解决,解决方案包含在 V1.6.1版本中。

尝试将这段代码添加到您的配置中。

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

你可以通过关闭 errorOnUnhandledRejections 来掩盖这个问题,但是这个错误说明你需要“处理一个可能的拒绝”,所以你只需要在你的承诺中添加一个附加条件。

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

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

但是 这只会关闭日志记录。错误本身将保持不变

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

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

相关链接:

也许不是你的特殊情况,但我也遇到过类似的问题。

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

修正压痕解决了这个问题。

为了避免在代码中多处键入额外的 .catch(function () {}),您可以向 $exceptionHandler添加一个 decorator

这是一个比其他选项更详细的选项,但您只需在一个地方进行更改。

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)时,我的问题得到了解决。

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

我没有用数组包装它们。

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

而不是

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

我希望这能帮某人节省点时间。