Angular 中数组的异步更新

Async update of array in Angular

本文关键字:异步 更新 数组 Angular      更新时间:2023-09-26

我有一个渲染推文的视图。在推文中,有user_mentions数组。从这个数组中,我想获取一个用户,向 API 发出另一个请求,获取用户的头像,然后渲染它。视图如下所示:

<md-item ng-repeat="item in content">
  <div class="md-tile-left" ng-if="item.show">
     <img ng-src="{{item.thumb}}" class="face" alt="{{item.name}}">
  </div>
  <div class="md-tile-content">
     {{item.text}}
  </div>
</md-item>

内容变量将包含所有推文。最初,content[i].show 对于所有 i 都是假的。控制器代码如下所示:

for(var i = 0; i < data.length; i++) {
  if(data[i].user_mentions[0]) {
     UserAvatar.get(data[i].user_mentions[0].screen_name).then(function(data){
        $scope.content[i].thumb = data.avatar;
        $scope.content[i].show = true;
     });
  }
}

现在,我面临的问题是,调用UserAvatar服务后,变量i将继续递增。因此,当到达回调时,我将具有 data.length 的值(因此,将抛出未定义的错误)。我通过将 i 传递给 API 然后将其取回找到了解决方法:

    UserAvatar.get(data[i].user_mentions[0].screen_name, i).then(function(data){
        $scope.content[i].thumb = data.avatar;
        $scope.content[i].show = true;
    });

我意识到这只是让它工作的黑客,但是有没有更聪明的方法呢?

每当调用一个函数时,你必须记住,当它被执行时,由外部函数创建的作用域中的所有变量(不仅是$scope,还有像 i 这样的外部变量)对函数都是可见的,我想你已经意识到了这一点,重要的是要意识到你实际上不需要在.then的处理程序中i你需要在索引i获取内容,但是你如何摆脱这种依赖?我提出两种可能的解决方案:

1)

function getAvatar(name, contentToUpdate) {
  return UserAvatar.get(name)
      .then(function (data) {
        contentToUpdate.thumb = data.avatar;
        contentToUpdate.show = true;
      });
}
for(var i = 0; i < data.length; i++) {
  if(data[i].user_mentions[0]) {
     getAvatar(data[i].user_mentions[0].screen_name, $scope.content[i]);
  }
}

当调用.then函数的处理程序时,i依赖项不再存在

2)

for(var i = 0; i < data.length; i++) {
  if(data[i].user_mentions[0]) {
     UserAvatar.get(data[i].user_mentions[0].screen_name).then((function(content) {
        return function (data) {
          content.thumb = data.avatar;
          content.show = true;
        }
     })($scope.content[i]));
  }
}
在此解决方案中,IIFE 返回一个函数,

该函数将成为处理程序,但 IIFE 实际上将捕获正在分析的内容的值,因此当返回的内部函数(这是 .thenonFulfilled处理程序)被调用时,没有i依赖关系

奖励:对我处理函数有很大帮助的一条规则如下:

函数将在定义它的位置执行

我不记得我在哪里读到它,我想它是在Nicholas Zakas的Web开发人员专业JavaScript中,强烈推荐:)

我意识到这只是使其工作的黑客,但是有没有 更聪明的方法?

正如@MukeshAgarwal在他的评论中所说,这不是真正的黑客,因为UserAvatar.get是一个异步过程。

因此,您必须在异步调用时捕获i的值。

您使用的方法还不错,但另一种方法是使用立即调用的函数:

不起作用

for (var i = 0; i < 10; i++) {
    // do something async
    setTimeout(function() { 
        console.log(i); // <- This will log 10 each time (the value of i at execution)
    }, 1000)
}

但这有效

for (var i = 0; i < 10; i++) {
    (function() {      
        var j = i;   // <- The value of i is captured in j. j is in a closure
        // do something async
        setTimeout(function() { 
            console.log(j);  // <- This will log 0, then 1, then 2 ... then 9 
        }, 1000)
    })()
}

问题是你试图在 for 循环中使用闭包,这并不像你发现的那样有效。您的黑客是修复它的一种选择。另一种方法是使用 forEach()/map() 或 IIFE ( (function(i) { ... })(i); ) 将循环包装在函数中。

在您的示例中,我将执行以下操作:

  var promises = data.map(function(item, index) {
     if (item.user_mentions[0]) {
        return UserAvatar.get(item.user_mentions[0].screen_name)
           .then(function(itemData) {
             $scope.content[index].thumb = itemData.avatar;
             $scope.content[index].show = true;
           });
     } else {
        return $q();
     }
  });
  $q.all(promises).then(function() {
     // All complete
  });