O_o
O_o

Reputation: 1103

variable value becomes undefined in NodeJS ExpressJS

I am working with NodeJS using ExpressJS framework in a mysql backend. I am running a query inside a for loop and my loop and work afterwards depends on the return value of the query. I am not very good with mysql query so I am running it through a for loop. The problem is, due asynchronous [I guess!], the for loop ends long before the query result comes out.

Here is my code:

function search_people_step2(user_id, search_criteria, user_friend)
{
  var first_name_friends = [];
  var last_name_friends = [];


  for(var i = 0; i < user_friend.length; i++)
  {
    con.query("SELECT first_name, second_name FROM user WHERE userid = ?", user_friend[i],function(err, rows)
   {
     if(err)
     {
       //error;
     }
     else
     {
       if(rows.length == 0)
       { 
         //nothing gets returned
       }
       else {
         console.log(rows);
         first_name_friends[i] = rows[0].first_name;
         last_name_friends[i] = rows[0].second_name;
       }
 }
 });
}

Now,I can get the value (using console.log) inside the query statement, however, on the outside, the value becomes empty (undefined) since the rest of the code has already been computed.

How can I solve this? Thanks in advance.

Upvotes: 0

Views: 1153

Answers (3)

Dinesh Agrawal
Dinesh Agrawal

Reputation: 346

simplest solution is 

function search_people_step2(user_id, search_criteria, user_friend)
    {
      var first_name_friends = [];
      var last_name_friends = [];


      for(var i = 0; i < user_friend.length; i++)
      {
        con.query("SELECT first_name, second_name FROM user WHERE userid = ?", user_friend[i],function(err, rows)
       {
         if(err)
         {
           //error;
         }
         else
         {
           if(rows.length == 0)
           { 
             //nothing gets returned
           }
           else {
             console.log(rows);
             first_name_friends[i] = rows[0].first_name;
             last_name_friends[i] = rows[0].second_name;
           }
          if(i==user_friend.length-1){
           //do your work here which you want to perform in end
          }
     }
     });
    }

or use async library

var async = require('async');
var first_name_friends = [];
var last_name_friends = [];

async.series([function(cb){
function search_people_step2(user_id, search_criteria, user_friend)
{
  for(var i = 0; i < user_friend.length; i++)
  {
    con.query("SELECT first_name, second_name FROM user WHERE userid = ?", user_friend[i],function(err, rows)
   {
     if(err)
     {
       //error;
     }
     else
     {
       if(rows.length == 0)
       { 
         //nothing gets returned
       }
       else {
         console.log(rows);
         first_name_friends[i] = rows[0].first_name;
         last_name_friends[i] = rows[0].second_name;
       }
if(i==user_friend.length-1){
       cb()
      }
 }
 });
}
},function(cb){
//do your work here 
}],function(err){})

Upvotes: 1

n00dl3
n00dl3

Reputation: 21584

The first thing that I find weird in your code is that you are not using an IN statement in your SQL query (not directly related to your problem though) which means you are making as many requests as there are entries in user_friend. The problem is that the SQL library is implemented asynchronously and you cannot avoid it. BUT you can handle it elegantly with Promises which are ES6 features: (I didn't test the code but I think it should work)

function search_people_step2(user_id, search_criteria, user_friend)
{
  return new Promise((resolve,reject)=>{
    var first_name_friends = [];
    var last_name_friends = [];
    var placeHolders=user_friend.map(()=>"?").join(",");
    con.query("SELECT first_name, second_name FROM user WHERE userid IN ("+placeHolders+")",user_friend,(err,rows)=>{
      if(err)
        reject(err);
      else{
        rows.forEach(row=>{
          first_name_friends.push(row.first_name);
          last_name_friends.push(row.second_name);
        });
        resolve({first_name_friends,last_name_friends});
      }
    });
  });
}

And call your function like this :

search_people_step2(id,crit,friends).then(result=>{
  //handle result asynchronously as there is no choice
  console.log(result.first_name_friends);
  console.log(result.last_name_friends);
}).catch(err=>{
  //handle error
});

Upvotes: 1

yoyobo
yoyobo

Reputation: 31

You are right, your problem is the asynchronous nature of the mysql call. You have to provide a callback to your search_people_step2 function. You may change it like this:

search_people_step2(user_id, search_criteria, user_friend, callback)

In your function body you may use a library called async to handle all the callbacks properly. Here is an example for the usage:

async.eachSeries(user_friend, function(item, eachCb){
    con.query("SELECT first_name, second_name FROM user WHERE userid = ?", 
       user_friend[i],function(err, rows) {
       if(err) {
         eachCb('error');
       }
       else {
         if(rows.length == 0){ 
           //nothing gets returned
           eachCb(null);
         }
         else {
           console.log(rows);
           first_name_friends.push(rows[0].first_name);
           last_name_friends.push(rows[0].second_name);
           eachCb(null);
        }
      }
   }, callback);
});

This calls each query in order on every item of the array and calls the inner callback if finished. When all items are processed or an error occured the outer callback is called. See the async library for further documentation.

Upvotes: 1

Related Questions