2

I am trying to loop over an array of devices and correctly fill a web page. But...

for (var i=0; i<t_devices.length; i++) {
            data_final = data.replace("%Title%",t_devices[i][1] );
            data_final = data_final.replace("%IP%", t_devices[i][0]);
            console.log(i);
            getStatus(t_devices[i][0], function(status) {
                console.log(i);
                data_final = data_final.replace("%Status%","<b>"+status+"</b>");
                $('#t-container').append(data_final);
            });
        }

Of course, this function is not working, as the for loop is completed before the callback function inside it, so data is messed up. Probably I am missing a trivial solution, but I am still struggling with this.

Fabio Marzocca
  • 1,573
  • 16
  • 36

5 Answers5

4

The simplest solution to this would be to create a closure around the callback, which you can do with an IIFE:

for (var i = 0; i < t_devices.length; i++) {
  data_final = data.replace("%Title%", t_devices[i][1]);
  data_final = data_final.replace("%IP%", t_devices[i][0]);
  console.log(i);
  
  (function(i, data_final) {
    getStatus(t_devices[i][0], function(status) {
      console.log(i);
      data_final = data_final.replace("%Status%", "<b>" + status + "</b>");
      $('#t-container').append(data_final);
    });
  }(i, data_final));
}
Rory McCrossan
  • 331,213
  • 40
  • 305
  • 339
1

If you use ES6, just replace var i with let i in your cycle declaration.

Otherwise modify your callback function

        for (var i=0; i<t_devices.length; i++) {
            data_final = data.replace("%Title%",t_devices[i][1] );
            data_final = data_final.replace("%IP%", t_devices[i][0]);
            console.log(i);
            getStatus(t_devices[i][0], function(index, status) {
                console.log(index);
                data_final = data_final.replace("%Status%","<b>"+status+"</b>");
                $('#t-container').append(data_final);
            }.bind(null, i));
              //first argument (null) is what would be 'this' 
              //inside function when it is called, but we don't need it
              //second (i) is what will be passed into callback as first argument
              //all other arguments will be passed as second, third and so on
        }
Gvz
  • 11
  • 2
0

Why don't you store the result of the first loop and work it after first loop has finished ?

let deviceStatusToWork = [];
for (let i=0; i<t_devices.length; i++) {
            data_final = data.replace("%Title%",t_devices[i][1] );
            data_final = data_final.replace("%IP%", t_devices[i][0]);
            console.log(i);
            deviceStatusToWork.push(t_devices[i][0])            
        }

for (let i = 0; i < deviceStatusWork; i++) {
getStatus(deviceStatusWork[i], function(status) {                
                data_final = data_final.replace("%Status%","<b>"+status+"</b>");
                $('#t-container').append(data_final);
            });
}
Mosè Raguzzini
  • 15,399
  • 1
  • 31
  • 43
0

I guess, if getStatus() returns a Promise, you can use async/await :

const myFunc = async () => {
    for (var i=0; i<t_devices.length; i++) {
        data_final = data.replace("%Title%",t_devices[i][1] );
        data_final = data_final.replace("%IP%", t_devices[i][0]);
        console.log(i);
        let status = await getStatus(t_devices[i][0]);
        console.log(i);
        data_final = data_final.replace("%Status%","<b>"+status+"</b>");
        $('#t-container').append(data_final);
    }
}

const getStatus = arg => new Promise( (resolve,reject) => {
    /*do stuff with arg*/
    resolve(status);
} );

myFunc();
Jeremy Thille
  • 26,047
  • 12
  • 43
  • 63
0

Since you're using jquery, you can use each utility. Your logic will run inside a closure. See https://api.jquery.com/jquery.each/

Have a look at this fiddle and see it working: https://jsfiddle.net/evm39o1s/

AlbertVanHalen
  • 632
  • 4
  • 12