Passing variable to promise in a loop

2019-03-07 23:21发布

问题:

I have a promise in a loop, and I don't know how to pass some scope variables into the promise handler.

for(var i in superarray){
    MyService.get(superarray[i].externalID).then(function(r){
        console.debug(i);
});

MyService is a working service, with a get method that return a promise.

app.factory('MyService', function($http,$q) {
  return {
     get : function(itemID){
        var deferred = $q.defer();
        $http.get('/someresturl/'+itemID).then(function(e) { 
                deferred.resolve(e.data);
        }, function(reason) {
                deferred.reject(reason);
        });
        return deferred.promise;
    }
});

In my console, the console.debug logicaly doesn't display 1,2,3,4,5. But 5,5,5,5,5. (there are 5 elements in my superarray).

How can I pass 'i' value in my promise scope, so I can use it in the then() ?

Is it possible?

回答1:

One way is to capture i in a closure :

for(var i in superarray) {
    (function(i) {
        MyService.get(superarray[i].externalID).then(function(r) {
            console.debug(i);
        });
    })(i);
}

Another way would be to arrange for itemID to be repeated back as a property of r :

for(var i in superarray){
    MyService.get(superarray[i].externalID).then(function(r) {
        console.debug(r.itemID);
    });
};


回答2:

By the time your callback is run, i will refer to the last element in your array. You can use a closure and capture the current value of i:

for (var i in superarray){
    (function(j) {
        MyService.get(superarray[j].externalID).then(function(r) {
            console.debug(j);
        });
    })(i);
}


回答3:

You can simplify the code a bit by using the built-in Array.prototype.forEach:

superarray.forEach(function (item, index) {
    MyService.get(item.externalID).then(function(r) {
        console.debug(index);
    });
});


回答4:

I would have just commented on the accepted solution but I don't currently have enough reputation.

I think that the second solution of repeating the itemID back as a property of r would work just fine as well.

simple promise handler:

angular.module('myApp', []).run(['MyService', function(MyService) {
  superarray = [1, 2, 3, 4, 5];


  for(var i in superarray) {
    MyService.get(superarray[i]).then(function(returned) {
      console.log(returned.id);
    });
  }

}]);

MyService returning itemID as a property of the returned object:

angular.module('myApp')
.factory('MyService', function($http,$q) {
  return {
     get : function(itemID){
        var deferred = $q.defer();

        $http.get('www.google.com').then(function(e) { 
                var returnObject = {
                  'data': e.data,
                  'id': itemID
                };
                deferred.resolve(returnObject);
        }, function(reason) {
                deferred.resolve(reason);
        });
        return deferred.promise;
    }
}});

Here is a working example on plnkr.



回答5:

Try this solution, I've used $q.all and I've added my parameter in array. It worked

   for(var i in superarray){
      $q.all([MyService.get(superarray[i].externalID), i]).then(function(results){
        var r = results[0];
        var i = results[1];
        console.debug(i);
      });
    }