Reputation: 424
In the following code, the val variable value keeps changing due to asynchronous behavior and generates unexpected output i.e. before connection query for first loop is finished, var variable according to second loop and generates wrong output.What is the best way to handle queries in loop that avoid idiosyncrasies caused by asynchronous nature.
var output = [];
for ( var j = 0; j < someArr.length ; j++ ) {
val = someArr[j];//some manipulation of someArr[j]
connection.query( "select id from someTable where someCol = ?",val, function(err, rows, fields) {
if ( err ) {
console.log( err );
} else {
output.push( rows[0].someVal );//push query output to this variable
}
});
}
console.log( output );//should contain output for all queries.
Upvotes: 3
Views: 11015
Reputation: 1762
Just use a closure to generate a temporary scope
var output;
for ( var j = 0; j < someArr.length ; j++ ) {
tVal = someArr[j];//some manipulation of someArr[j]
(function(val){
connection.query( "select id from someTable where someCol = ?",val, function(err, rows, fields) {
if ( err ) {
console.log( err );
} else {
output.push( rows[0].someVal );//push query output to this variable
}
});
})(tVal);
}
JavaScript is a single threaded language, meaning only one task can be executed at a time. When the JavaScript interpreter initially executes code, it first enters into a global execution context by default. Each invocation of a function from this point on will result in the creation of a new execution context.
For each execution context there is a scope chain coupled with it. The scope chain contains the variable object for every execution context in the execution stack. It is used for determining variable access and identifier resolution.
The anonymous function help us to create a new scope "blocking" the value of tVal
because when is executed a new scope
is added to the scope chain
containing val
value.
This new scope
is a child of the parent scope
in wich the for loop
is executed, when the cycle continues tVal
change but val
is contained in the child scope
and is safe from the variation.
Upvotes: 5
Reputation: 295
Try "let" instead of "var" This might work
var output = [];
for ( let j = 0; j < someArr.length ; j++ ) {
val = someArr[j];//some manipulation of someArr[j]
connection.query( "select id from someTable where someCol = ?",val, function(err, rows, fields) {
if ( err ) {
console.log( err );
} else {
output.push( rows[0].someVal );//push query output to this variable
}
});
}
Upvotes: 3
Reputation: 1507
The best & standard way to use loop & inside the that execute query is use async.eachSeries.
So using async.eachSeries it will execute one by one in series & once foreach complete you can use callback.
Ref : http://caolan.github.io/async/docs.html#eachSeries
var async = require('async');
var output = [];
connection.query('SELECT * FROM tablename LIMIT 10',function(error,results,filelds){
if(error) throw err;
async.eachSeries(results,function(data,callback){ // It will be executed one by one
//Here it will be wait query execute. It will work like synchronous
connection.query('SELECT * FROM tablename where id = 1',function(error,results1,filelds){
if(error) throw err;
output.push(results1[0].id)
callback();
});
}, function(err, results) {
console.log(output); // Output will the value that you have inserted in array, once for loop completed ex . 1,2,3,4,5,6,7,8,9
});
})
Upvotes: 3