【问题标题】:Trouble with asynchronous events in angular角度异步事件的问题
【发布时间】:2016-03-14 01:34:17
【问题描述】:

我的代码循环遍历数组中的 10 个项目,对每个项目发出请求,然后将返回的数据推送到数组中。在$q.all 行之前一切正常。

details.getDetails = function(idSet, pageNum) {
  var page = idSet[pageNum],
      mainDeferred = $q.defer(),
      promises = [],
      combinedItems = [];

  for(var i=0; i<page.length; i++){
    var deferred = $q.defer();
    ngGPlacesAPI.placeDetails({placeId: page[i][i]})
      .then(function(data){
        combinedItems.push(data);
        console.log(combinedItems); /// This shows the objects being pushed into the array
        deferred.resolve();
      });

    promises.push(deferred.promise);
  }

  console.log(promises); /// This shows the 10 promises

  $q.all(promises).then(function() { /// Nothing after this line runs
    console.log('test', mainDeferred.promise); /// This logs nothing
    mainDeferred.resolve(combinedItems);
  });
  return mainDeferred.promise;
};

【问题讨论】:

    标签: javascript angularjs angular-promise


    【解决方案1】:

    这是在 javascript 中处理异步时非常常见的错误。

    应该问的问题是:“当解析器(您传递给.then 的函数)查找变量deferred 时,它会返回什么变量?”。那么答案是,它们都引用了您声明的最后一个deferred

    问题是,您在 resolve 函数之外声明了 deferred。记住 javascript 是如何查找变量的:

    1. 变量(在我们的例子中为deferred)是否在直接函数范围内可用? (在我们的例子中,没有)
    2. 向上遍历父作用域,直到找到具有给定名称的变量。

    到解析器触发时,您已重新声明 deferred 10 次,每个声明都会覆盖前一个声明!所以每次解析器触发时,它实际上都会解析 same deferred!

    答案是将您的deferred 声明包装在一个闭包中:

    for(var i=0; i<page.length; i++){
      (function(){
        var deferred = $q.defer();
        ngGPlacesAPI.placeDetails({placeId: page[i][i]})
          .then(function(data){
            combinedItems.push(data);
            console.log(combinedItems); /// This shows the objects being pushed into the array
            deferred.resolve();
          });
        promises.push(deferred.promise);
      })()
    }
    

    但实际上,您可以简化整个程序并避免延迟。让我知道这对您是否有意义!:

    details.getDetails = function(idSet, pageNum) {
      var page = idSet[pageNum];
    
      // generate an array from 0 to page.length
      var items = Array.apply(null, { length: page.length })
    
      var promises = items.map(function (i) {
        return ngGPlacesAPI.placeDetails({placeId: page[i][i]});
      })
    
      return $q.all(promises)
    };
    

    【讨论】:

    • 摆脱deferreds 肯定是正确的做法。但是,就我个人而言,我会发现一个香草循环,您可以将承诺推送到一个数组以传递给$q.all,这比您在这里编写的Array.apply + items.map 解决方案更容易使用。
    • 非常感谢您的详细回答。这真的帮助我理解和学习了一些新事物。您的第二个解决方案尤其使我的轮子转动,最终使我得到了答案。 @mgilson 也感谢您帮助我找到解决方案。我最终像你提到的那样循环,因为我在 array.map 上的索引遇到了问题
    • @mgilson 这可能是代码风格的问题。我更喜欢函数式迭代器,但有些人发现过程循环更容易理解。各有千秋!
    猜你喜欢
    • 2021-06-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多