【问题标题】:Async update of array in AngularAngular中数组的异步更新
【发布时间】:2015-03-08 14:35:06
【问题描述】:

我有一个渲染推文的视图。在推文中,有 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 变量将包含所有推文。最初, 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;
    });

我知道这只是一个让它工作的技巧,但有没有更聪明的方法呢?

【问题讨论】:

  • 这不是黑客攻击,因为获取头像是异步过程,所以 i 值会增加。如果对您来说变得困难,您也可以使用异步库
  • @MukeshAgarwal:谢谢!所以你认为这是可以在生产环境中使用的东西吗?

标签: javascript angularjs asynchronous


【解决方案1】:

每当调用一个函数时,你必须记住,当它执行时,由外部函数创建的范围内的所有变量(不仅是$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 的Professional JavaScript for Web Developers,强烈推荐 :)

【讨论】:

  • @MauricioPope:谢谢!我真的很喜欢你提供的第二种解决方案。
【解决方案2】:

我意识到这只是一个让它工作的技巧,但是有没有 更聪明的做法?

正如@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)
    })()

}

【讨论】:

    【解决方案3】:

    问题是您尝试在 for 循环中使用闭包,但您发现它不起作用。您的 hack 是修复它的一种选择。另一种方法是使用 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
      });
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-04-16
      • 2022-11-22
      • 1970-01-01
      • 2018-02-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-12-31
      相关资源
      最近更新 更多