在AngularJS中的嵌套异步调用中交换数据

在AngularJS中的嵌套异步调用中交换数据,angularjs,rest,tags,promise,Angularjs,Rest,Tags,Promise,我是AngularJS的新手,我面临着一个有趣的问题: 我创建了一个Angular factory服务,作为我正在调用的REST服务的服务代理。 我的服务代理如下所示: angular.module('myApp').factory('myService', ['$http', function($http) { var baseAddr = "http://myservice.com"; var token = "secure_identifier"; var My

我是AngularJS的新手,我面临着一个有趣的问题:

我创建了一个Angular factory服务,作为我正在调用的REST服务的服务代理。 我的服务代理如下所示:

angular.module('myApp').factory('myService', ['$http', function($http) {

    var baseAddr = "http://myservice.com";
    var token = "secure_identifier";

    var MyService = {
        doRequest: function(method, path, token, postData) {
            return $http({method: method, url: baseAddr+path, headers: {....}, data:postData });
        }
    }

    return {

        GetGroupUsers: function( id)
        {
            return MyService.doRequest('GET', '/api/groupusers/'+id+'.json', token, null);
        },
        GetUser: function( id)
        {
            return MyService.doRequest('GET', '/api/users/'+id+'.json', token, null);
        }
    }
}]);
因此,GetGroupUsers返回给定组中的userID-s数组,GetUser返回 指定的成员(例如电子邮件地址、全名、用户名、出生日期)。 我想要的是查询一个组(获取该组中的userid-s)并将用户的名称附加到数组项 在GetUser(userid)方法的帮助下。 我有一个控制器,在其中我通过我的代理服务查询这个服务。以下是控制器的相关部分:

//...
MyService.GetGroupUsers(GroupId).then(function(userlist)
{
    //userlist is an array of a complex object that consists of userId-s and groupId-s
    //Suppose that it looks like this:
    // [ {uid: 1, groupId: 1}, {uid: 2, groupId: 1}, {uid: 3, groupId: 1},...,.. ]
    $scope.groups = userlist.data;

    //but i need to attach for every item in the array the name of the selected user
    // [ {uid: 1, groupId: 1,username: 'Alga Rithem'}, {uid: 2, groupId: 1, username:'Anna Nimmity'}, {uid: 3, groupId: 1, username:'Anna Conda'},...,.. ]

    //So I iterate over the userlist
    for (var i = 0; i < userlist.data.length; i++)
    {
        MyService.GetUser(userlist.data[i].uid).
            then(function(user)
            {
                //And here is the problem I'm facing with:
                //The value of variable "i" becomes obsolate, because GetUser call is async to the for-loop
                // when the "then" "callback" executes, the loop may has been finished already!
                //So I dont really know, where to put the value I've just received
                //I need that value of i, what it was when i called the service!               
                $scope.groups[i].name = user.data.name;  //So this is WRONG!
            });
    }
});

//...
现在我可以在我的控制器中检索有效的、过去的I值:

//...
MyService.GetGroupUsers(GroupId).then(function(userlist)
{
    //userlist is an array of a complex object that consists of userId-s and groupId-s
    //Suppose that it looks like this:
    // [ {uid: 1, groupId: 1}, {uid: 2, groupId: 1}, {uid: 3, groupId: 1},...,.. ]
    $scope.groups = userlist.data;

    //but i need to attach for every item in the array the name of the selected user
    // [ {uid: 1, groupId: 1,username: 'Alga Rithem'}, {uid: 2, groupId: 1, username:'Anna Nimmity'}, {uid: 3, groupId: 1, username:'Anna Conda'},...,.. ]

    //So I iterate over the userlist
    for (var i = 0; i < userlist.data.length; i++)
    {
        MyService.GetUser(userlist.data[i].uid).
            then(function(user)
            {
                //And here is the problem I'm facing with:
                //The value of variable "i" becomes obsolate, because GetUser call is async to the for-loop
                // when the "then" "callback" executes, the loop may has been finished already!
                //So I dont really know, where to put the value I've just received
                //I need that value of i, what it was when i called the service!               
                $scope.groups[i].name = user.data.name;  //So this is WRONG!
            });
    }
});

//...
修改的控制器代码:

//...

MyService.GetGroupUsers(GroupId).then(function(userlist)
{    
    $scope.groups = userlist.data;

    for (var i = 0; i < userlist.data.length; i++)
    {
        MyService.GetUser(adat.data[i].uid).
            then(function(user)
            {                
                $scope.groups[parseInt(user.config.tag)].name = user.data.name;  //This Works fine!
            });
    }
});
/。。。
GetGroupUsers(GroupId).then(函数(userlist)
{    
$scope.groups=userlist.data;
对于(var i=0;i
我的问题是:这个解决方案在技术上正确吗?解决此类问题的最佳做法是什么? AngularJS有这样的工具吗

ps:在这种情况下,修改REST服务API以返回用户名并不是一个解决方案


提前感谢

您可以使用
.forEach
循环,而不是关闭
i
for
循环:

 userlist.data.forEach(function(user, i){
        MyService.GetUser(user.uid).
            then(function(user)
            {
                $scope.groups[i].name = user.data.name;  //So this is WRONG!
            });
    }
或者,您可以使用
.map
创建映射,并使用
$q.all
一次性进行所有更新:

 $q.all(userlist.data.map(function(user){
        return MyService.GetUser(user.uid))
 }).then(function(users){
       // assign to scope, users is an array here and you can use indices
 });

虽然真正实用的解决方案不是为
n
用户调用
n
,而是创建一种方法,在服务器上接受多个UID,并同时为他们获取所有数据,因为它不会进行
n
往返和http请求,因此系统负载更少,执行更快。

如果您的API是REST,考虑使用<代码> $Reals,这使得事情变得简单多了。另外,能否准确显示所有信息
GetUser
返回值?假设GetUser返回一个JSON对象,如下所示:“{”id“:9,“email”:”myemail@mycompany.com“,”用户名“:”我的昵称“}”$resource如何让我更轻松?无论如何,我需要在某个地方“连接”这些对象。是吗?谢谢你的回答。您的第一个示例不会减慢网页的加载时间吗?我的意思是forEach使服务调用同步,所以控制器将等待forEach完成,这可能非常耗时。在您的第二个解决方案中,我如何检索标记?你能给我链接一篇关于这种数据映射的文章吗?我真的不明白这件事。谢谢。@bkotyik它正在做
。forEach
而不是
for
并不会神奇地使调用同步,它只是同步地进行调用(注意区别,调用仍然是异步进行的)。在第二个示例中,您可以通过将
users[i]与$scope.groups[i]
匹配来检索索引,因为您拥有整个数组。你的解决方案确实改变了后端API。好的,明白了。谢谢大家!+1个问题如果您不介意的话:如果这些服务调用中的任何一个在序列中失败,我如何知道哪一个用户查询失败了?您可以将
.catch
处理程序附加到承诺中-请参阅