Trouble with asynchronous events in angular

烈酒焚心 提交于 2019-12-12 13:07:17

问题


The code I have loops through 10 items in an array, makes a request for each item, then pushes the returned data to an array. Everything runs fine until the $q.all line.

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;
};

回答1:


This is a pretty common error when dealing with async in javascript.

The question should should ask is: "when the resolver (the function you pass to .then) looks up the variable deferred, what variable does it get back?". Then answer is, they all reference the very last deferred you declare.

The problem is, you're declaring deferred outside of your resolve function. Remember how javascript looks up variables:

  1. Is the variable (in our case, deferred) available in the immediate function scope? (in our case, no)
  2. Traverse up parent scopes until we find a variable with the given name.

By the time the resolver fires, you've redeclared deferred 10 times, each declaration overwriting the previous one! So each time a resolver fires, it actually resolves the same deferred!

The answer is to wrap your deferred declaration in a closure:

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);
  })()
}

But really, you can simplify your whole program and avoid the deferreds. Let me know if this makes sense to you!:

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)
};


来源:https://stackoverflow.com/questions/35978172/trouble-with-asynchronous-events-in-angular

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!