Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Node Mysql async multiple queries

Tags:

node.js

mysql

I was wondering what's the best way to handle nested mysql-queries is nodejs.

So something like this:

connection.query("SELECT * FROM blogs ORDER BY time DESC", function(err, blogs, fields) {

   for (blog in blogs) {

        connection.query("SELECT * FROM tags WHERE blog_id='blog.id' ", function(err, tags, fields) {

        blog.tags = tags

     });

   }

   res.send(blogs)

});

This obviously doesn't work, because of the async nature. The result already gets returned before the tags are fetched.

I've been reading up on node and callbacks and promises seems to be the way to go. But I'm unable to see how I would best use them in this small example.

Thx!

like image 681
mhtsbt Avatar asked Jun 15 '14 17:06

mhtsbt


3 Answers

So you have to wait for all callbacks to return before you send the response. If we ignore error handling and empty results for simplicity this can be done similar to:

var callback = function(blogs) {
    res.send(blogs);
}

connection.query("SELECT * FROM blogs ORDER BY time DESC", function(err, blogs, fields) {
    var pending = blogs.length;

   for (blog in blogs) {

        connection.query("SELECT * FROM tags WHERE blog_id='blog.id' ", function(err, tags, fields) {
        blog.tags = tags;

        if (0 === --pending) {
            callback(blogs);
        }
     });
   }
});

With promises, look into Promise.all function which returns a new promise. This promises is resolved when all promises passed to it in the array are resolved. With the Q library it should be something like:

var getTags = function(blog) {
    var deferred = Q.defer();
    connection.query("SELECT * FROM tags WHERE blog_id='blog.id' ", function(err, tags, fields) {
        blog.tags = tags;
        deferred.resolve();
    });
    return deferred.promise;
}

var promises = blogs.map(getTags(blog));

Q.all(promises).then(res.send(blogs));
like image 182
Alexei Avatar answered Oct 05 '22 11:10

Alexei


You can use async.series module(to iterate over the queries`)

async.eachSeries(blogs,
                function (query, callback) {

                    connection.query(blogs.blog, "SELECT * FROM tags WHERE blog_id='blog.id'" , function (err, result) {
                        if (err) {

                            //throw err;
                            callback(err, null);
                            return;

                        } else {
                            console.log('Query executed successfully');
                            blogs.blog.tags = tags;
                        }
                        callback(null, blogs.blog);

                    });

                },

                function finalCallback(err, results) {

                            return callback(null, results);
                        });
                    }
                });

        });



    });`

You can enhance this with and query.on feature (to process each row)

like image 35
randomness Avatar answered Oct 05 '22 10:10

randomness


You can try something like this;

connection.query("SELECT * FROM blogs ORDER BY time DESC", fetchedBlogs);

function fetchedBlogs(err, blogs, fields) {

    if (err || !blogs || !blogs.length) {
        // an error occurred or no blogs available
        // handle error or send empty blog array
        return res.send();
    }

    var count = blogs.length;

    (function iterate(i) {
            if (i === count) {
                // all blogs processed!
                return res.send(blogs);
            }

            var blog = blogs[i];

            connection.query("SELECT * FROM tags WHERE blog_id='" + blog.id +
                "'", fetchedTags);

            function fetchedTags(err, tags, fields) {
                blog.tags = tags;
                iterate(i+1);
            });
    })(0);
}

Hope this helps.

like image 26
vmx Avatar answered Oct 05 '22 11:10

vmx