【问题标题】:AngularJS: multiple promises resolving erraticallyAngularJS:多个承诺不规律地解决
【发布时间】:2013-11-05 20:43:39
【问题描述】:

我在控制器中将两个集合分配到我的范围内;两者都来自包装 RestAngular 并返回承诺的服务。每个单独返回我想要的集合,但是当我将两者都分配给 $scope 时,它​​们中的一个永远不会解决(无论它们的顺序如何,都是同一个失败)。

如果我通过再次调用有问题的服务登录到控制台,那么你瞧,原来的分配工作完美。

这两个服务的工作方式几乎相同:

.factory('Role', ['Restangular', '$q', function(Restangular, $q){
        var _collection = [];
        var _roleService = Restangular.all('roles');

        return {
            getList: function() {
                // return _roleService.getList();
                var listDeferred = $q.defer();
                _roleService.getList()
                .then(function(list) {
                    listDeferred.resolve(list);
                    _collection = list;
                });
                return listDeferred.promise;
            }
        }
    }
])

并且控制器指定如下(我已将 console.log 保留在其中,但删除它会导致 $scope.roles 永远无法解析):

.controller('ResourceCtrl', ['$scope', 'Resource', 'Role', function($scope, Resource, Role) {
    $scope.roles = Role.getList();
    $scope.resources = Resource.getList();

    console.log(Role.getList());
}])

一切都是在 Angular 中完成的,所以我不认为我错过了 $apply - 但除非我偶然发现了一个错误,否则我的代码肯定有问题......

编辑

我找到了导致错误的原因,但没有解决方案。我的 Resource 服务与 Role 一非常相似,但由于 Resources 具有 Roles,我将 Role 注入 Resource 并使用它将各个元素链接在一起。一旦我删除了那行,所有范围/承诺的东西都回来了。

我想这让我回到:如何可靠地将服务链接在一起(即,具有角色字段的资源可以在服务级别将相应的角色对象附加到它们)。

这是我的资源:

.factory('Resource', ['Restangular', '$q', 'Role',
    function(Restangular, $q, Role){
        var _resourceService = RestAngular.all('resources');
        var _roleService = Role;

        var _convertObjectsToUrls = function(item) {
            for (var property in item) {
                if (item.hasOwnProperty(property) && typeof(item[property]) == 'object' && item[property] != null) {
                    item[property] = item[property].url;
                }
            }
            return item;
        }

        var _convertUrlsToObjects = function(item) {
            for (var property in item) {
                if (item.hasOwnProperty(property) && typeof(item[property]) == 'string' && item[property] != '' && property != 'url' && item[property].substr(0,4) == 'http') {
 /* THIS LINE BREAKS IT */                       item[property] = _roleService.getByUrl(item[property]);
                }
            }
            return item;
        }

        var _getIdFromUrl = function(url) {
            var pathElements = url.split('/')
            return pathElements[pathElements.length - 2]
        }

        var _cleanParams = function(item) {
            for (var property in item) {
                if (item.hasOwnProperty(property)) {
                    item[property] = undefined;
                }
            }
            return item;
        }

        return {
            add: function(item) {
                var responseDeferred = $q.defer();
                item = _convertObjectsToUrls(item);

                _resourceService.post(item)
                .then(function(response){
                    response = _convertUrlsToObjects(response);
                    response.name = response.first_name + ' ' + response.last_name;

                    _collection.push(response);
                    responseDeferred.resolve(response);

                    item = _cleanParams(item);
                });
                return responseDeferred.promise;
            },
            edit: function(item) {
                var responseDeferred = $q.defer();
                var idx = _collection.indexOf(item);
                item = _convertObjectsToUrls(item);

                item.customPUT(_getIdFromUrl(item.url))
                .then(function(response){
                    response = _convertUrlsToObjects(response);
                    response.name = response.first_name + ' ' + response.last_name;

                    _collection.splice(idx, 1, response)
                    responseDeferred.resolve(response);
                });
                return responseDeferred.promise;
            },
            delete: function(item) {
                var responseDeferred = $q.defer();
                var idx = _collection.indexOf(item);
                // item = _convertObjectsToUrls(item);

                item.customDELETE(_getIdFromUrl(item.url), {})
                .then(function(response){
                    response = _convertUrlsToObjects(response);

                    _collection.splice(idx, 1)
                    responseDeferred.resolve(response);
                });
                return responseDeferred.promise;
            },
            getList: function() {
                var listDeferred = $q.defer();
                var list = _resourceService.getList()
                .then(function(list) {
                    _.each(list, function(item, index, list){
                        item = _convertUrlsToObjects(item);
                        item.name = item.first_name + ' ' + item.last_name;
                    })
                    listDeferred.resolve(list);
                    _collection = list;
                });
                return listDeferred.promise;
            }
        }
    }
])

【问题讨论】:

    标签: angularjs restangular


    【解决方案1】:

    不确定资源服务来自何处以及是否重要。由于您在角色服务中返回了一个承诺,您可以这样做:

    .controller('ResourceCtrl', ['$scope', 'Role', function($scope, Role) {
        Role.getList().then(function(roles){
             $scope.roles = roles;
        });
    }])
    

    或者,您可以让 Angular 为您解析角色(例如,如果您只是在模板中使用角色)

    .controller('ResourceCtrl', ['$scope', 'Role', function($scope, Role) {
          $scope.roles = Role.getList();
     }])
    

    编辑:我还注意到var _collection = []; 似乎不需要。也许您还可以发布资源服务的代码? 您的角色服务的这段代码应该足够了。

    .factory('Role', ['Restangular', '$q', function(Restangular, $q){
            return {
                getList: function() {
                    var listDeferred = $q.defer();
                    Restangular.all('roles').getList().then(function(list) {
                        listDeferred.resolve(list);
                    });
                    return listDeferred.promise;
                }
            }
        }
    ])
    

    进一步编辑:

    这个 plunkr 表明,在 angular 1.2rc3 中,第一种方法有效: http://embed.plnkr.co/SU5UMK7jNffXWnWiV1HE/preview

    但是,如果您要尝试利用 promise 的自动取消交换功能(在 rc3 中已弃用,那么您就不走运了): http://embed.plnkr.co/zdepkLCesYkj8pXYFTN2/preview

    【讨论】:

    • 您的第二个建议与 OP 的做法有何不同?
    • 实际上并没有太大的不同,只是很微妙。您会注意到没有注入资源服务。这是因为,我相信,如果 Resource 服务以与 Role 服务相同的方式完成,则注入顺序会覆盖下划线服务定义(例如 _roleService)。
    • 我对AngularJS了解不多,但是如果控制器构造函数的注入顺序和工厂构造函数的注入顺序有什么关系,我会觉得奇怪。
    • 我省略了资源服务,因为除了 Restangular 配置之外,它与此时的角色基本相同。我尝试了将集合分配给范围的其他方法(例如在then 中)无济于事。删除 _roleService var 似乎也无济于事。有谁明白为什么console.log 调用(或任何进一步的调用)会导致它工作?
    • 听起来像是 Angular 中的错误。你用的是什么版本?
    猜你喜欢
    • 1970-01-01
    • 2016-01-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多