Alan Alan - 5 months ago 11
SQL Question

Callback until for loop with query is done

I posted a question before and realized my problem actually was async functions. I managed to work out most of it, but I got one little problem left. Using async I used waterfall to create an order for the some queries...

exports.getMenu = function(id_restaurant, callback){
async.waterfall([
async.apply(firstQuery, id_restaurant),
secondQuery,
thirdQuery,
fourthQuery,
formMenu
], function(err, result){
if(err){
console.log(err);
}

callback(result);
});
};


Everything works until
fourthQuery
, where I have to loop to get all dishes of a menu.

function fourthQuery(array_totalP, array_nombresSecc, array_secciones, callback){
var size = array_nombresSecc.length;
var array_secciones = array_secciones;
var array_nombresSecc = array_nombresSecc;

var dishes = [];

pool.getConnection(function(err, connection) {

if(err) {
console.log(err);
callback(true);
return;
}
for (var i = 0; i < size; i++) {
connection.query("SELECT name, price FROM menu_product WHERE id_seccion = ? AND active = 1", [array_secciones[i]],
function(err, results2) {
if(err) {
console.log(err);
callback(true);
return;
}

console.log("Result query 4 " + JSON.stringify(results2));
dishes[i] = results2;
console.log("VALOR PLATILLOS EN i : " + JSON.stringify(dishes[i]));
// this prints the result but only if it has a value over 2
});
};
}); // pool

console.log("I'm sending " + dishes); // this logs an empty array

callback(null, dishes, array_nombresSecc);
};


So what i can see that happens from printing the value of 'i' each loop is that it always has the value of 2. Because that's 'size' value. Also, even though it's saving results of index '2' I believe the callback is being done even before the for loop is done, because my fifth function is recieving an empty array.

How can i make my code wait to callback until my for loop is done?

NOTE: Sorry, part of my code is in spanish, tried to translate the important parts of it.

Answer

There are a few ways to handle this, one is to look into promise architecture. Promise.all will let you supply one callback to handle the values from each child promise.

To use what you've already got, however, I'd push the values into your dishes array, rather than assigning them specifically to i indexes, then check the size of that array at the end of each connection. When the array length matches the size, fire the callback. (as seen below)

If you need a way to tie each result to that specific i value, I'd recommend pushing them as an object

dishes.push({'index': i, 'dish': results2})

Afterward, if you need the array of just dishes, you can sort the array by that index value and run a map function.

dishes.sort(function(a,b){ return a.index - b.index; })
dishes = dishes.map(function(a){ return a.dish })

Here's the code adjusted:

function fourthQuery(array_totalP, array_nombresSecc, array_secciones, callback) {
  var size = array_nombresSecc.length;
  var array_secciones = array_secciones;
  var array_nombresSecc = array_nombresSecc;

  var dishes = [];

  pool.getConnection(function(err, connection) {

    if (err) {
      console.log(err);
      callback(true);
      return;
    }
    for (var i = 0; i < size; i++) {
      connection.query("SELECT name, price FROM menu_product WHERE id_seccion = ? AND active = 1", [array_secciones[i]],
        function(err, results2) {
          if (err) {
            console.log(err);
            callback(true);
            return;
          }

          console.log("Result query 4 " + JSON.stringify(results2));
        
        
          dishes.push(results2)
          
          if(dishes.length == size){
            console.log("I'm sending " + dishes);
            callback(null, dishes, array_nombresSecc)
          }
        
        
          console.log("VALOR PLATILLOS EN i : " + JSON.stringify(dishes[i]));
          // this prints the result but only if it has a value over 2
        });
    };
  }); // pool

  ;
};