patrick
patrick

Reputation: 380

foreach doing operation before mysql insert

I still struggle with understanding how the steps are run.

Basically i am doing a foreach computing color then insert it in a database with normal foreach code :

var mysql = require("mysql"),
    namer = require('color-namer'),
    async = require('async'); 
var connection = mysql.createConnection({
  multipleStatements: true,
  host     : 'localhost',
  user     : 'root',
  password : 'root',
  database : 'xxxx'});
connection.connect();




connection.query(
    `SELECT color from sg_fashion_colors limit 10`, 
    function(err, results, fields) {
        results.forEach(function(elem){

            var currentHex = elem['color'],
                currentColor = namer(currentHex);
                console.log(currentHex);
            connection.query(
                `INSERT INTO name_color_pat_test (hex, basic) VALUES (?,?);`, 
                [currentHex,currentColor['basic'][0]['name']] ,
                function(err,data){
                    console.log("insert");   
                }              
            );  
        });
    }
);

but instead of doing

color 
insert
color
insert

It does

color
color
insert 
insert 

I just don't understand the mechanism on why it will first foreach on the color then foreach on the insert.

For information i was able to produce the expected output that i want using async

connection.query(
    `SELECT color from sg_fashion_colors limit 10`, 
    function(err, results, fields) {
        async.eachSeries(results, function (elem, seriesCallback) {
                var currentHex = elem['color'],
                currentColor = namer(currentHex);
                console.log(currentHex);
                connection.query( `INSERT INTO name_color_pat_test (hex, basic) VALUES (?,?);`, // insert the SKU inside a database with their primary color
                    [currentHex,currentColor['basic'][0]['name']] ,
                    function(err, results, fields) {
                        if (err) throw err;
                        console.log("insert");
                        seriesCallback(null);
                });                           
        }, function(responsetoendofloop){
            console.log("everything has run");   
        });         
    }
);

Upvotes: 0

Views: 215

Answers (1)

Farid Nouri Neshat
Farid Nouri Neshat

Reputation: 30410

Let me just show another simplified case here:

var colors = ['blue', 'red', 'green'];
colors.forEach(function(color) {
    console.log('before setTimeout', color);
    setTimeout(function () {
        console.log('after setTimeout', color);
    }, 0);
});

If I try it in the Chrome console here's the output:

VM1119:3 before setTimeout blue
VM1119:3 before setTimeout red
VM1119:3 before setTimeout green
<- undefined
VM1119:5 after setTimeout blue
VM1119:5 after setTimeout red
VM1119:5 after setTimeout green

Same thing that happened in your script. setTimeout is similar to connection.query as they both call their callback asynchronously, which means they will only do it when the current stack has finished executing and they will call the callback function. So in both cases the forEach continues to loop and once done then the callbacks are executed in order they were scheduled. Note the the undefined is printed there when colors.forEach is finished executing and it represents the return value of the function. Try changing it to colors.map instead.

Let me give you another example:

setTimeout(function () { console.log('hello'); }, 0)
while (true) {};

As you see the setTimeout is before the infinite while loop and it is meant to be executed immediately(0 milliseconds after), but it will never execute. When the javascript VM reads the first line, it will schedule the callback to be called later, then it will read the second line and it will get stuck there. setTimeout callback will never be called till the everything else in the stack has finished executing.

Now same thing happens for connection.query. It's callback is scheduled to be called later. It also might take a dozen miliseconds or more(depending on the network and database) to be called. So by the time that it's called everything else is already have executed.

Now async.eachSeries doesn't iterate like the forEach. It waits till it's callback is called, not matter how long it takes.

So if you call callback outside of the connection.query callback then probably might get the same results as the forEach(this is bad code, don't change your code, it's just for your education):

    async.eachSeries(results, function (elem, seriesCallback) {
            var currentHex = elem['color'],
            currentColor = namer(currentHex);
            console.log(currentHex);
            connection.query( `INSERT INTO name_color_pat_test (hex, basic) VALUES (?,?);`, // insert the SKU inside a database with their primary color
                [currentHex,currentColor['basic'][0]['name']] ,
                function(err, results, fields) {
                    if (err) throw err;
                    console.log("insert");

            });

            seriesCallback(null);
       });                           

This happens because now seriesCallback before connection.query result is ready, even though it's placed after it.

Feel free to ask more questions in the comments if still not clear.

Upvotes: 1

Related Questions