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);
});
第一个选项只是通过按照建议在 $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(...)
我希望这可以节省一些时间。
我们的 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);
});
第一个选项只是通过按照建议在 $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(...)
我希望这可以节省一些时间。