phantom phantom - 8 days ago 6
Javascript Question

Node. get data from database and and make post request with results

I have a list of users in a database. They have initially been given an access_token, when signing up to db, but as this expires I need to create a new access_token through a refresh_token which has also been saved in the db against a user.

I need to make a POST request to an endpoint to access get a new access token.

I am trying to loop over each row in the database and get a new access_token this way. When there is one user it works perfectly, but when there is more than one user in the

users
array each
user
is the same, i.e. the last user in the database table. I guess the request isn't finished and it is trying to send a new request or something like that.

Does anyone know how to get around this?

app.get('/get-users', function (req, res) {
connection.query('SELECT * from users', function(err, rows, fields) {
if(err) console.log(err);

for(var i = 0; i < rows.length; i++) {
name = rows[i].name;
userId = rows[i].userId;
console.log(userId);// this gives the correct userid
var authOptions = {
url: url,
headers: {
'Authorization': 'Basic ' + (new Buffer(client_id + ':' + client_secret).toString('base64'))
},
form: {
grant_type: 'refresh_token',
refresh_token: refresh_token
},
json: true
};

request.post(authOptions, function(error, response, body) {
if (!error && response.statusCode === 200) {
var access_token = body.access_token;
users.push({name: name, userId: userId, accessToken: access_token});
console.log(userId)//gives last entry in db
}
});
setTimeout(function () {
console.log(users)
}, 3000)
}
});
res.send('getting here');
})

rsp rsp
Answer

Your problem is this:

    for(var i = 0; i < rows.length; i++) {
        name = rows[i].name;
        userId = rows[i].userId;
        // ...
        request.post(authOptions, function(error, response, body) {
          // you're using name and userId here
        });
        // and also you use users here:
        console.log(users)
    }

Now, the problem is that no callback will get called (even for the first iteration) before all of the loop iterations finish.

So first of all, the console.log(users) will always get the value of users whatever it was before any users.push() took place.

Additionally, for every callback the value of name and userId will be the same - whatever it was after the last iteration of your for loop.

Your name and userId seem to be global variables, or they are defined in some outer scope. What you need to do is to use let to define them locally, so that they get fresh bindings for every loop iteration.

See a simple example to demonstrate what going on here:

for (var i = 0; i < 4; i++) {
  x = 'Number ' + i;
  setTimeout(function () {
    console.log(x);
  }, 500*i);
}

It's not good because x is a global variable. Now with var:

for (var i = 0; i < 4; i++) {
  var x = 'Number ' + i;
  setTimeout(function () {
    console.log(x);
  }, 500*i);
}

Still not good because there is only one bonding for x shared for every iterations. Now with let:

for (var i = 0; i < 4; i++) {
  let x = 'Number ' + i;
  setTimeout(function () {
    console.log(x);
  }, 500*i);
}

It works because every iteration gets a new binding.

Update

Another thing is making sure that the you know when every request.post has ended so that you can send a response at the right time.

You can use async to help you with that. You can use promises and libraries like Bluebird or Q to help you with that. But you can also do a simple trick. If you want to know that you are inside of the last callback on the example that I showed above, you can count the invocations like this:

let current = 0, last = 0;
for (let i = 0; i < 4; i++) {
  let x = 'Number ' + i;
  setTimeout(function () {
    if (++current === last) console.log('Last callback:');
    console.log(x);
  }, 500*i);
  last++;
}

This may not be the best way to handle complex situations but it demonstrates what is going on: the last variable gets incremented for every iteration and the callbacks are only registered at that time. Then the callbacks start firing one after another and you can increment the current variable every time a ne callback is fired and compare if that was the last one or not.

Now, a general advice: Use let (or const) every time instead of var. It's much more intuitive how it works. Of course you can use var if you know what you're doing but when in doubt use let.