Alan
Alan

Reputation: 379

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.

Upvotes: 0

Views: 101

Answers (2)

Joe Turgeon
Joe Turgeon

Reputation: 95

Since you're already using the async, I would suggest replacing the for() loop in fourthQuery with async.each().

The updated fourthQuery would look like this:

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; 
        }
        async.each(array_secciones,
            function(item, itemCallback) {
                // Function fun for each item in array_secciones
                connection.query("SELECT name, price FROM menu_product WHERE id_seccion = ? AND active = 1", [item], 
                    function(err, results2) {
                        if(err) { 
                            console.log(err); 
                            return itemCallback(true);
                        }

                        console.log("Result query 4 " + JSON.stringify(results2));
                        dishes.push(results2);
                        console.log("VALOR PLATILLOS EN i : " + JSON.stringify(dishes[dishes.length-1])); 
                        // this prints the result but only if it has a value over 2
                        return itemCallback();
                    });
            },
            function(err) {
                // Function run after all items in array are processed or an error occurs
                console.log("I'm sending " + dishes); // this logs an empty array
                callback(null, dishes, array_nombresSecc);
            });
    }); // pool
};

Alternatively, you can use async.map(), which handles gathering the results in the final callback so doesn't rely on the dishes variable.

Upvotes: 0

jmcgriz
jmcgriz

Reputation: 3368

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

  ;
};

Upvotes: 1

Related Questions