在嵌套的承诺链中重新抛出异常

Rethrowing exceptions in nested promise chains

我正在尝试找到一种干净的方式来链接 AngularJs 中的承诺。我的目标是不使用 $q 重新包装 $http 承诺,而是利用 $http.xxx() 方法 return 承诺和使用链接这一事实。在下面的代码中,我试图满足以下用例。

注意:如果我删除内部 .catch() 块,我的 404 将被外部 catch() 捕获。我试过使用 then(actionHandler, errorHandler) 方法,也试过 returning 来自 .catch() 的字符串并尝试在 .catch() 中使用 this.reject()。

Give $scope.username is Bret 
When calling getPosts() and getting a 404 from /users 
Then scope.error has a message indicating that the user wasn't found

//代码

angular.module('app', [])
    .controller('MainController', function ($scope, $log, $q, $http) {
        $scope.posts = [];
        $scope.message = "";

        function getUserByUserName(userName) {

            return getUsers()
                .then(function (response) {

                    var user;

                    for (var i = 0; i < response.data.length; i++) {
                        if (response.data[i].username === userName) {
                            user = response.data[i];
                            break;
                        }
                    }

                    return user;
                }).catch(function (error) {
                    throw "User could not be found.";
                });
        };

        function getUsers() {
            return $http.get('http://jsonplaceholder.typicode.com/users');
        }

        function getPostByUser(user){

            return getPosts()
                .then(function (posts) {

                    var postsByUser = [];

                    for (var i = 0; i < posts.length; i++) {
                        if (posts[i].userId === user.id) {
                            postsByUser.push(posts[i]);
                        }
                    }

                    return postsByUser;
                });
        }

        function getPosts() {
            return $http.get('http://jsonplaceholder.typicode.com/posts')
                .then(function (response) {
                    return response.data;
                });
        };

        function addPostsToScope(posts) {

            $log.debug('MainController.getPostByUserName reponse.length: ' + posts.length);

            $scope.posts.length = 0;

            $scope.posts.username = $scope.username;

            for (var i = 0; i < posts.length; i++) {
                $scope.posts.push(posts[i]);
            }
        };

        $scope.getPosts = function () {
            getUserByUserName($scope.username)
                .then(getPostByUser)
                .then(addPostsToScope)
                .catch(function (error) {
                        $scope.error = error;
                });
        };
    });

// 测试

describe('MainController', function () {

var endpointController;

var dummyPosts = [
    {
        "userId": 1,
        "id": 1,
        "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
        "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
    },
    {
        "userId": 1,
        "id": 2,
        "title": "qui est esse",
        "body": "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla"
    },
    {
        "userId": 2,
        "id": 11,
        "title": "et ea vero quia laudantium autem",
        "body": "delectus reiciendis molestiae occaecati non minima eveniet qui voluptatibus\naccusamus in eum beatae sit\nvel qui neque voluptates ut commodi qui incidunt\nut animi commodi"
    }];

var dummyUsers = [
    {
        "id": 1,
        "name": "Leanne Graham",
        "username": "Bret",
        "email": "Sincere@april.biz",
        "address": {
            "street": "Kulas Light",
            "suite": "Apt. 556",
            "city": "Gwenborough",
            "zipcode": "92998-3874",
            "geo": {
                "lat": "-37.3159",
                "lng": "81.1496"
            }
        },
        "phone": "1-770-736-8031 x56442",
        "website": "hildegard.org",
        "company": {
            "name": "Romaguera-Crona",
            "catchPhrase": "Multi-layered client-server neural-net",
            "bs": "harness real-time e-markets"
        }
    },
    {
        "id": 2,
        "name": "Ervin Howell",
        "username": "Antonette",
        "email": "Shanna@melissa.tv",
        "address": {
            "street": "Victor Plains",
            "suite": "Suite 879",
            "city": "Wisokyburgh",
            "zipcode": "90566-7771",
            "geo": {
                "lat": "-43.9509",
                "lng": "-34.4618"
            }
        },
        "phone": "010-692-6593 x09125",
        "website": "anastasia.net",
        "company": {
            "name": "Deckow-Crist",
            "catchPhrase": "Proactive didactic contingency",
            "bs": "synergize scalable supply-chains"
        }
    }];

beforeEach(module('app'));

it('Give $scope.username is Bret When calling getPosts() and getting a 404 from /users Then scope.error has a message indicating that the user wasnt found',
    inject(function ($rootScope, $controller, $httpBackend) {

        $httpBackend.whenGET('http://jsonplaceholder.typicode.com/users')
            .respond(404, null, null, "Not Found");
        $httpBackend.whenGET('http://jsonplaceholder.typicode.com/posts')
            .respond(dummyPosts);

        var scope = $rootScope.$new();

        scope.username = 'Bret';

        endpointController = $controller("MainController", { $scope: scope });

        scope.getPosts();

        $httpBackend.flush();

        expect(endpointController).toBeTruthy();
        expect(scope.error).toBeTruthy();
        expect(scope.error).toBe("User could not be found.");
    }));

});

编辑 我在这里添加了一个 plunker 来显示问题:http://plnkr.co/edit/URMFfeM9IHPUGDbydZfa?p=preview

预期的控制台输出是

That user could not be found.
outer catch block reached!

但它正在写出来

That user could not be found.
getPosts
getPosts.Then()
getPostsByUser()

在 catch 中使用 return $q.reject(error)

return getUsers()
    .then(function (response) {
        var user;

        for (var i = 0; i < response.data.length; i++) {
            if (response.data[i].username === userName) {
                user = response.data[i];
                break;
            }
        }

        return user;
    }).catch(function (error) {
        return $q.reject(error);
    });

Plunkr 带有一个演示捕获异常并重新抛出异常的小演示。