Mohammed Aouf Zouag
Mohammed Aouf Zouag

Reputation: 17142

NodeJS scope issue

var std;
......
...... // here I fill std with some data
......

I'm trying to access the 'std' array while handling this route:

app.post('/saveABS',function(req,res){
var fileName = "./public/"+f;
fs.writeFile(fileName, JSON.stringify(std, null, 4), function(err) {
    if(err) {
        console.log(err);
    } else {
        console.log("The file was saved !");
        console.log(a);
        for(var i = 0; i < std.length; i++)
        {
            var connection = mysql.createConnection({
                host     : 'localhost',
                user     : 'root',
                password : 'admin' ,
                database : 'pfe'
            });
            console.log('before hr_cours = ' + std[i].hr_cours); // <========= WORKS

Here it displays the value of std[i].hr_cours correctly.

            if(std[i].hr_cours != a[i].hr_cours)
            {

                connection.connect(function(error){
                    console.log('after hr_cours = ' + std[i].hr_cours); // <============ undefined

Here it displays undefined.

                    if(error)
                    {
                        console.log('Unable to connect to database.');
                    }
                    else
                    {
                        console.log("i= " + i);
                        console.log("I'm in : hr_cours = " + std[i].hr_cours);
                        connection.query("INSERT INTO assiste VALUES (CURDATE(),(SELECT id_etud FROM Etudiant WHERE nom_etud = ?),(SELECT s.id_seanceFROM Seance s, aura_lieu a, jour j, fait_objet fWHERE s.id_seance = a.id_seanceAND CURTIME() BETWEEN a.heure_debut AND a.heure_fin AND a.id_jour = j.id_jour AND j.dat = CURDATE() AND f.id_seance = s.id_seance AND f.id_ens = ?), ?)", [std[i].nom_etud, idEns, std[i].hr_cours], function(er1, res1){
                            if(er1)
                            {
                                console.log('Query error: ');
                            }
                            else
                            {
                                console.log('success');
                            }
                        });
                    }
                });
            }
        }
        res.end("all");
    }
});
});

I noticed that the issue is triggered once the execution flow enters the connection.connect block and that it's a scope issue; I searched everywhere for the answer but couldn't find a solution to my specific problem. How can I make this work?

Upvotes: 0

Views: 115

Answers (2)

bloodyKnuckles
bloodyKnuckles

Reputation: 12089

The execution of connection.connect is delayed and therefore when it fires i is not the same value as when a reference to i was passed in to it by the for loop. So, to lock in the value of i in that particular loop execution, wrap the connection.connect method and callback in an IIFE and pass i as it's parameter:

~function(i) {
    connection.connect(function(error){
        console.log('after hr_cours = ' + std[i].hr_cours);
        // other stuff
    })
}(i)

A more detailed explanation of IIFE's and scope closure in my answer here: Odd javascript code execution order

And a JSFiddle playground to explore the technique: IIFE Scope Closure With Pre-Existing Parameters.

Upvotes: 1

laggingreflex
laggingreflex

Reputation: 34677

Here's your problem:

app.post('/saveABS', function(req, res) {
    // ...
    for (var i = 0; i < std.length; i++) {                
        // ... 
        // This is SYNC. Here i = 0,1,2... 
        connection.connect(function(error) {
            // This is A-SYNC. 
            // Here i = std.length + 1  
            // always!
            // No matter even if it syntactically looks like i should be 1,2,3 according to the loop
            // but all of this is executed way after the loop has long been completed

            // ...
            // Here it displays undefined.
            console.log('after hr_cours = ' + std[i].hr_cours); // <============ undefined

A solution: Have a self executing anonymous function (function(){})() inside the loop and pass it the arguments (i) so that they will stay the same

for (var i = 0; i < std.length; i++) (function(i){
    connection.connect(function(error) {
        // ...
        // Here, since i is an argument passed to outer function
        // i will be i,2,3... 
})(i);

Another solution: forEach loop, which already has a callback function

std.forEach(function(currentValue, i, std){
    connection.connect(function(error) {
        // ...
        // Same here, since i is an argument passed to outer function
        // i will be i,2,3...     
        // Also currentValue = std[i] so you can use that 
});

Upvotes: 1

Related Questions