0

I have absolutely no idea of whats happening. But when I try to iterate in this, using [i] on line 12, it doesn´t work and I get the span I want to write in totally blank. But, without changing anything else, only switching [i] to [0] on line 12, for example, I get my span written as expected!

Any ideas?

$(function() {  
    var listaCoisas = [
        "disruptiva",
        "matadora",
        "feroz",
        "tradicional"
    ];

    var i;
    for (i=0; i<listaCoisas.length; i++){
        setTimeout(function(){
            $('#word-attribute').empty().append(listaCoisas[3]);
        },1000);
    }
});
skellertor
  • 916
  • 1
  • 9
  • 26
diegodacal
  • 97
  • 1
  • 10
  • 1
    setTimeout is asynchronous .. by the time the code in the callback is called, `i == listaCoisas.length` for modern browsers, change `var i; for (i=0;...` to `for let i=0;...` or, try using `listaCoisas.forEach` instead – Jaromanda X Aug 16 '17 at 01:13
  • The thing is that I really needed to use settimeout (or any delay). Is there a way to make it synchronous? – diegodacal Aug 16 '17 at 01:24
  • Did you even try either suggestion? You can still use settimeout – Jaromanda X Aug 16 '17 at 01:25
  • i tried the foreach and didnt work. im investigating about for let. sorry for asking before trying both. – diegodacal Aug 16 '17 at 01:28
  • You possibly used foreach wrong – Jaromanda X Aug 16 '17 at 01:28
  • `listaCoisas.forEach(function(entry){ setTimeout(function(){ $('#word-attribute').empty().append(entry); },1000); });` – diegodacal Aug 16 '17 at 01:29
  • that looks correct, what's wrong with it? are you expecting each iteration to be completed 1 second after the previous? `listaCoisas.forEach(function(entry, i){ setTimeout(function(){ $('#word-attribute').empty().append(entry); },(i+1) * 1000); });` – Jaromanda X Aug 16 '17 at 01:31
  • exactly that. thats why i wanted to make settimeout synchronous. – diegodacal Aug 16 '17 at 01:32
  • you can't change asynchronous to synchronous - edited previous comment to show the simplistic way to do what you want – Jaromanda X Aug 16 '17 at 01:32

2 Answers2

0

As you want each iteration to be 1 second after the previous, you need to change the setTimeout delay - using forEach

listaCoisas.forEach(function(entry, i){ 
    setTimeout(function(){ 
        $('#word-attribute').empty().append(entry); 
    },(i+1) * 1000); 
});
Jaromanda X
  • 53,868
  • 5
  • 73
  • 87
  • Got it! The idea is to sort of set all timings until callback runs. thanks! – diegodacal Aug 16 '17 at 01:36
  • 1
    well, the idea is to separate each iteration of `$('#word-attribute').empty().append(entry);` by 1 second - so the first runs after 1 second, the second after 2 seconds, etc all the setTimeout's are "started" at almost the same time – Jaromanda X Aug 16 '17 at 01:38
0

The above answer works well when it replaces the for loop in your code but adding the staggered delays alone would not make the original code above work and I think it is important to understand why.

Because of closures, by the time your first setTimeout is called, the for loop will have already run and the value of i will be one more than the last item in your array (4th index). This means nothing will ever get appended to #word-attribute as it will try to append an item that doesn't exist. If you open up the console here you can see the problem.

There are a couple of things you could do if you wanted to keep the for loop beyond adding the delayed setTimeouts:

  1. If you are compiling your javascript using something like Babel then you can take advantage of the ES6 let keyword which carries its own closure with it. Using let instead of var for i. Example
  2. Use an anonymous function to create another closure around the i variable which we will call x inside the anonymous function. setTimeout will then have access to the correct value of i when it is called. Example
Scott L
  • 549
  • 1
  • 6
  • 13