0

I have a loop that gets an array of users and then tries to get the information of each one. With this code below, if I have two users, it returns the same information (the last user) twice. Therefore, when I try to update and save the user information, it only saves the last user's information.

for (i = 0; i < users.length; i++) { 
    var amount = users[i].monthlyAmount;
    // var stripeID = users[i].stripeID;
    // if the amount charged is greater than 0, charge the customer the correct amount
    if(amount != '0') {
      var stripeID = users[i].stripeID;
      accountID =  users[i];
      // stripe function to charge customer
      stripe.charges.create({
        amount: amount,
        currency: "usd",
        customer: stripeID, 
        description: "Monthly charge"
        }, function(err, charge) {
        if (err) return (err);
        // if there are no errors with charging the customer, proceed to reset the monthly amount
        if(!err) {
          console.log(accountID);
          accountID.monthlyAmount = '0';
          accountID.save();
        }
      });
    }
  }

Here's what the output of this is:

{ billingInfo: 'true',
createdAt: Sun Nov 16 2014 14:05:21 GMT-0600 (CST),
email: 'a@a.com',
encryptedPassword: '*removed',
monthlyAmount: 1000,
propertyCount: 0,
stripeID: '*removed',
updatedAt: Sun Nov 16 2014 15:10:59 GMT-0600 (CST),
id: '54690381c03a265b07c99564' }
{ billingInfo: 'true',
createdAt: Sun Nov 16 2014 14:05:21 GMT-0600 (CST),
email: 'a@a.com',
encryptedPassword: '*removed',
monthlyAmount: '0',
propertyCount: 0,
stripeID: '*removed',
updatedAt: Sun Nov 16 2014 15:10:59 GMT-0600 (CST),
id: '54690381c03a265b07c99564' }
parkeragee
  • 313
  • 1
  • 3
  • 14
  • I've tried using accountID as a non-global variable as well, but each user becomes undefined. – parkeragee Nov 16 '14 at 21:34
  • 1
    The function you're defining refers to AccountID, which is not local to the function itself. So it will load whatever value that has at the time the function runs. Since that is presumably after your for loop finishes, it is always the value that accountID had when the loop finishes - i.e. the one for the last user. – The Archetypal Paul Nov 16 '14 at 21:45
  • And if you make AccountID local to the function containing the for-loop, then by the time the charging function runs, that other function has finished and so AccountID is undefined – The Archetypal Paul Nov 16 '14 at 21:47

2 Answers2

2

Just create a closure so you maintain the scope of the "i" you are reiterating, and you should be good. Because you are looping, the value of "i" is the last value set to it, so create a closure - a walled garden for your values. See Aravinds post for explanations

for (i = 0; i < users.length; i++) {
  (function(i){
    var amount = users[i].monthlyAmount;
    // var stripeID = users[i].stripeID;
    // if the amount charged is greater than 0, charge the customer the correct amount
    if(amount != '0') {
      var stripeID = users[i].stripeID;
      accountID =  users[i];
      // stripe function to charge customer
      stripe.charges.create({
        amount: amount,
        currency: "usd",
        customer: stripeID, 
        description: "Monthly charge"
        }, function(err, charge) {
        if (err) return (err);
        // if there are no errors with charging the customer, proceed to reset the monthly amount
        if(!err) {
          console.log(accountID);
          accountID.monthlyAmount = '0';
          accountID.save();
        }
      });
    }
  }(i))
  }
james emanon
  • 11,185
  • 11
  • 56
  • 97
1

This is because you are doing asynchronous function calls, which goes like this.

for (i = 0; i < users.length; i++) { 
      stripe.charges.create({
           ...
        }, function(err, charge) {
            //when stripes.charges.create function is done, call this piece of code.
        });
}
  1. i is set to 0
  2. stripe is called for users[0].stripeID, and while the function is processing, we don't wait, we proceed to step (3).
  3. i is set to 1
  4. stripe is called for users[1].stripeID, and while this function call is processing, we don't wait, we proceed to step (5).
  5. At this point we have finished the for loop.
  6. Oh, now the function call (2) is completed, and we do the callback function. In the callback, we refer to accountID, which is the current accountID, which is in fact the accountID we set recently, in step (4).
  7. And now the step (4) comepletes, so we do it's callback and display the current accountID, which is the recent accountID, which is again the one we got in step (4).

Okay, how do we fix this?

for (i = 0; i < users.length; i++) { 
    var amount = users[i].monthlyAmount;
    // var stripeID = users[i].stripeID;
    // if the amount charged is greater than 0, charge the customer the correct amount
    if(amount != '0') {
      var stripeID = users[i].stripeID;
      accountID =  users[i];
      function callback(){
        var accountID = accountID;
        return function(err, charge) {
        if (err) return (err);
        // if there are no errors with charging the customer, proceed to reset the monthly amount
        if(!err) {
          console.log(accountID);
          accountID.monthlyAmount = '0';
          accountID.save();
        };
      }
      // stripe function to charge customer
      stripe.charges.create({
        amount: amount,
        currency: "usd",
        customer: stripeID, 
        description: "Monthly charge"
        }, callback()
      });
    }
  }
Aravind
  • 3,169
  • 3
  • 23
  • 37