Angular 1.6.0:"可能未处理的拒绝"错误

75

我们在Angular应用程序中解决Promise的模式一直有效,直到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中)中为拒绝的承诺发出“可能未处理的拒绝”错误。但是我们在调用我们的错误服务的第二个函数中处理了拒绝。

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


1
我也注意到了我们代码库中的这个问题。奇怪的是,使用Chrome启动器运行测试套件却没有问题。PhantomJS才是在抱怨。 - Mario Tacke
如果您收到此消息并且没有刚刚升级,请按照以下步骤检查您的Angular版本: https://dev59.com/1mQo5IYBdhLWcg3wfPUa - hubatish
被接受的答案建议隐藏错误。重复建议使用更强大的替代方案,此答案提供了更好的解决方法。 - georgeawg
11个回答

66

尝试将此代码添加到您的配置文件中。我曾经遇到过类似的问题,这个解决方法很有效。

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

67
我必须指出,这只是掩盖错误,开发人员表示代码中存在某些问题,它只是掩盖了错误。 - Sachin Sharma
3
同意,虽然它可能回避了问题,但根本原因仍然存在,开发人员应该处理错误情况。 - Skrew
2
具体是哪个配置文件? - Hobbamok

25

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

然而,当您调用.then的承诺成功时,它会调用第一个回调函数。如果此回调函数引发异常或返回拒绝的承诺,则将不会处理此结果的拒绝,因为第二个回调函数不处理由第一个回调函数引起的拒绝。这仅是符合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

好的观点!是的,意图是处理在调用.then之前发生的拒绝。触发此拒绝的代码(在Angular 1.5.9中有效)已添加到原始问题中。 - Groucho
3
如何实际修复所询问的模式代码? - vip
6
好的,修复方法类似于$promise.then(success).catch(error),其中catch捕获所有错误,更多信息请参见“注意”部分,网址为migration - vip
1
这真的应该是正确的答案。 - tcmoore
2
如果您的函数设计返回一个被拒绝的 Promise,该怎么办?类似于 $http.get() 返回一个 Promise,如果请求失败,则该 Promise 被设计为被拒绝。我有一些返回 Promise 的函数,在某些情况下它们可以返回 $q.reject(err),但似乎 Angular 不喜欢这样,除非我忽略警告? - Ryan Silva

22

第一种选择是通过配置$qProvider来禁用errorOnUnhandledRejections,以实现隐藏错误,如Cengkuru Michael所建议的。

但是这只会关闭日志记录。错误本身仍然存在。

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

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

链接:


19

通过回滚到 Angular 1.5.9 并重新运行测试,找到了问题所在。这是一个简单的注入问题,但 Angular 1.6.0 却用 "可能未处理拒绝" 错误代替了它,掩盖了实际错误。


目前这对我来说是一个大问题,因为我正在迁移代码库并替换旧的注入依赖项。你找到了一种防止混淆并使其抛出真实错误的方法吗? - ChrisJ
上方的最佳答案可以在运行代码中修复它,但是我无法让它在Karma中抛出错误,除非回滚到Angular 1.5.9版本。 - Groucho

8
为了避免在代码中多处输入额外的 .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);
        };
    }
};

5

你可以通过关闭errorOnUnhandledRejections来遮掩这个问题,但错误提示说你需要“处理可能的拒绝”,所以你只需要在promise中添加一个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


3

3
仍然在1.6.2版本中遇到这个问题。 - Daniel Bonnell
1
虽然这个链接可能回答了问题,但最好在此处包含答案的基本部分并提供参考链接。如果链接页面更改,仅链接的答案可能会失效。- [来自审查] (/ review / low-quality-posts / 18725685) - Mostafa Berg
@MostafaTorbjørnBerg 谢谢。我更新了答案。 - Piotr Pradzynski

2

我注意到在测试执行期间出现了同样的行为。奇怪的是,在生产环境中代码运行良好,只有在测试中失败。

让你的测试变得更加容易的解决方案是将 catch(angular.noop) 添加到你的 promise 模拟中。对于上面的例子,它应该像这样:

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


2

在更新到Angular 1.6.7后,我也遇到了同样的问题,但是当我查看代码时,发现对于我的情况,错误是由$interval.cancel(interval);引起的。

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


1
我在进行一些更改后也出现了同样的通知。原来是因为我从使用angularjs的$http请求切换到使用多个请求的$q服务,但我没有将它们包装在数组中。例如:
$q.all(request1, request2).then(...) 

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

我希望这可以为某些人节省时间。

(保留html标记)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接