0

I have a angular service inside a for loop that returns an array of object. I want to get the summation of the value returned by that service but I got nothing in the end. My service works fine but my problem is I can't get the summation. Below is my code

controller

var TotalOBValueLand = 0;
for(var i = 0; i < $scope.selectedProp.length; i++){
     AccountService.getTopAccountDetails($scope.selectedProp[i]["propId"]).then(function(msg){
           TotalOBValueLand += parseInt(msg.data[0].OBValueLand);
           //my return data here has no error.
     });
}
console.log(TotalOBValueLand); //I got zero;
pryxen
  • 381
  • 2
  • 22
  • 1
    `.getTopAccountDetails()` is asynchronous - the for loop is completing prior to the resolution of the promise, so you are logging `0` – hackerrdave Feb 16 '17 at 02:19
  • Possible duplicate of [Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference](http://stackoverflow.com/questions/23667086/why-is-my-variable-unaltered-after-i-modify-it-inside-of-a-function-asynchron) – Adam Jenkins Feb 16 '17 at 02:28

3 Answers3

3

Use Promise.all and array#map to get an array of results, then use Array#reduce to sum them up

var TotalOBValueLand = 0;
Promise.all($scope.selectedProp.map(function(prop) {
     return AccountService.getTopAccountDetails(prop).then(function(msg){
           return parseInt(msg.data[0].OBValueLand);
     });
})).then(function(results) {
    TotalOBValueLand = results.reduce(function(a, b) {
       return a + b; 
    });
    console.log(TotalOBValueLand);
});

In response to the comments

var TotalOBValueLand = 0;
var TotalOBValueBuilding = 0;
Promise.all($scope.selectedProp.map(function(prop) {
     return AccountService.getTopAccountDetails(prop).then(function(msg){
           return parseInt(msg.data[0]);
     });
})).then(function(results) {
    TotalOBValueLand = results.reduce(function(a, b) {
       return a.OBValueLand + b.OBValueLand; 
    });
    TotalOBValueBuilding  = results.reduce(function(a, b) {
       return a.OBValueBuilding  + b.OBValueBuilding ; 
    });
    console.log(TotalOBValueLand, TotalOBValueBuilding);
});

and a little more generic

Promise.all($scope.selectedProp.map(function(prop) {
     return AccountService.getTopAccountDetails(prop).then(function(msg){
           return parseInt(msg.data[0]);
     });
})).then(function(results) {
    var totals = results.reduce(function(result, a) {
        Object.keys(a).forEach(function(key) {
            result[key] = (result[key] || 0) + a[key];
        });
        return result;
    }, {});
    console.log(totals.OBValueLand, totals.OBValueBuilding);
});
Jaromanda X
  • 53,868
  • 5
  • 73
  • 87
  • sir I have another question what should I do if I have to return more value in `AccountService.getTopAccountDetails(prop)`? In your answer you only return the `parseInt(msg.data[0].OBValueLand);` in `AccountService.getTopAccountDetails(prop)` – pryxen Feb 16 '17 at 02:41
  • Well that's the only value your code uses. So I'd be wildly speculating an answer to that – Jaromanda X Feb 16 '17 at 02:43
  • Your answer is very useful sir but I have more value to calculate like `OBValueBuilding` and many more. :( – pryxen Feb 16 '17 at 02:55
1

You cannot access console.log(TotalOBValueLand); outside the response since .getTopAccountDetails() is asynchronous, it will be always 0.

try to wrap it inside,

var TotalOBValueLand = 0;
for(var i = 0; i < $scope.selectedProp.length; i++){
     AccountService.getTopAccountDetails($scope.selectedProp[i]["propId"]).then(function(msg){
           TotalOBValueLand += parseInt(msg.data[0].OBValueLand);
           console.log(TotalOBValueLand);  
     });
}
Sajeetharan
  • 216,225
  • 63
  • 350
  • 396
0

The problem is that you are mixing asynchronous and synchronous functions. This should demonstrate what is going on a little for you

https://jsfiddle.net/Austio/v7goqk4d/

AccountService = {
  getTopAccountDetails: function() {
    return new Promise((resolve) => resolve(1))
  }
}

var TotalOBValueLand = 0;
for(var i = 0; i < 2; i++){
     AccountService.getTopAccountDetails().then(function(x){
       TotalOBValueLand += x;
       console.log('incremented async', TotalOBValueLand)
     });
}
console.log('sync', TotalOBValueLand);
setTimeout(() => 
console.log('timeout', TotalOBValueLand), 2000)

Solution using an array of promises that we resolve

var TotalOBValueLand = 0;
promises = []
for(var i = 0; i < 2; i++){
  promise = AccountService
    .getTopAccountDetails()
  promises.push(promise)
}
console.log('before', TotalOBValueLand);

Promise
  .all(promises)
  .then(results => {
    TotalOBValueLand = results.reduce((curr,acc) => curr + acc, 0);
    console.log('done', TotalOBValueLand);
    return TotalOBValueLand;
  })
  .catch(err => 'handle me')
Austio
  • 5,939
  • 20
  • 34
  • So what is your suggested solution? To use a timeout and *hope* that all of the async results are in by the time the timeout runs? – nnnnnn Feb 16 '17 at 02:43
  • No that was just demonstrating the issue, you can't depend on when if ever those will resolve. Without rearchitecting, I would use await for this https://www.npmjs.com/package/await – Austio Feb 16 '17 at 03:05