0

I need to create a small delay in this for loop:

for (i = 1; i <= cloneIndex; i++) {
                        var myElem = document.getElementById('form' + i);
                        if (myElem != null) {
                            function postData() {
                                return {
                                udd: document.getElementById('udd').value,
                                data: date_in,
                                hora_ini: hour_in,
                                hora_fim: hour_out,
                                cat: $('#form' + i).find('select[id="cat"]').val(),
                                m1: $('#form' + i).find('select[id="q1"]').val(),
                                m2: $('#form' + i).find('select[id="q2"]').val(),
                                m3: $('#form' + i).find('select[id="q3"]').val(),
                                m4: $('#form' + i).find('select[id="q4"]').val(),
                                m5: $('#form' + i).find('select[id="q5"]').val()
                                }
                            }

                            var newItem = postData();
                            $2sxc(@Dnn.Module.ModuleID).webApi.post('app/auto/content/audits', {}, newItem);
                        }
            }

Following stackoverflow examples, I tried this solution:

for (i = 1; i <= cloneIndex; i++) {
                (function(i){
                    setTimeout(function(){
                        var myElem = document.getElementById('form' + i);
                        if (myElem != null) {
                            function postData() {
                                return {
                                udd: document.getElementById('udd').value,
                                data: date_in,
                                hora_ini: hour_in,
                                hora_fim: hour_out,
                                cat: $('#form' + i).find('select[id="cat"]').val(),
                                m1: $('#form' + i).find('select[id="q1"]').val(),
                                m2: $('#form' + i).find('select[id="q2"]').val(),
                                m3: $('#form' + i).find('select[id="q3"]').val(),
                                m4: $('#form' + i).find('select[id="q4"]').val(),
                                m5: $('#form' + i).find('select[id="q5"]').val()
                                }
                            }

                            var newItem = postData();
                            $2sxc(Dnn.Module.ModuleID).webApi.post('app/auto/content/audits', {}, newItem);
                        }
                    }, 1000 * i);
                }(i));
            }

However this breaks the function inside. It seems myElem is now always null. Too many "i"s? How can I fix this?

Gerardo Furtado
  • 100,839
  • 9
  • 121
  • 171
João Gomes
  • 312
  • 2
  • 12
  • A `for` loop will run almost immediately to the end. Creating such delays is not a simple matter. Have a look at my answer here: https://stackoverflow.com/a/37563825/5768908 – Gerardo Furtado Oct 29 '17 at 23:51
  • In wich scope is your variable `i` declared? *(explicitely or implicitely)* And did you check the value of `i` when the function is executed? – Thomas Oct 30 '17 at 00:00
  • Also doesn't work Gerard. "i" is just used for the loop. Without the delay code, the original code works fine ( https://pastebin.mozilla.org/9071510 ), it cycles through form1, form2, etc. I only need the delay since the server can't handle 10 posts simultaneously. – João Gomes Oct 30 '17 at 00:14
  • @Bergi How is this a duplicate? Did you even read it? That solution does not work! – João Gomes Oct 30 '17 at 08:16
  • Oops, I looked like you were having issues with the asynchrony. Regarding your question why `myElem` is `null`, we don't know the rest of your code or your page markup. It certainly looks like the `#form1`… elements that `document.getElementById` is selecting do exist in your document when the loop is running, but not later when the timeout callbacks run. – Bergi Oct 30 '17 at 16:40
  • what happens if you do `console.log(i)` inside the setTimeout? Does it print the index or not? – Our_Benefactors Oct 30 '17 at 16:50

2 Answers2

1

You need to define the variable inside the closure for it to be unique to each iteration:

for (var i = 1; i < 10; i++) {
  (function() {
    var k = i; // <-- k will be different for each iteration, because it was declared inside the closure. i was defined outside the closure.
    setTimeout(function() {
      console.log("Delayed: ", i, k)
    }, i * 1000)
  }());
}

...or else include i in the closure definition:

for (var i = 1; i < 10; i++) {
  (function(i) {
    setTimeout(function() {
      console.log("Delayed: ", i)
    }, i * 1000)
  }(i));
}
Daniel Beck
  • 20,653
  • 5
  • 38
  • 53
0

Nevermind. The reason the code did not work was simple. The rest of the code below did not wait for the delayed loop to end, so it actually broke the function.

This fixed it (placed inside the setTimeout function):

k++;
if (k == cloneIndex) {rest of the code that needs the loop to end}
João Gomes
  • 312
  • 2
  • 12
  • Beware, you're effectively defining a global variable `k` here and incrementing it inside the setTimeout -- if you were to run this loop a second time it would start at whatever value it left off at the first time around. – Daniel Beck Oct 30 '17 at 17:19
  • That's the point. It only increases after each delayed loop. But knows the count outside it. – João Gomes Oct 31 '17 at 07:44