0

I'm having problems with setTimeout. I've also tried setInterval. I wish JS just had a pause or sleep.

In theory, this code should write the first link from the array, wait 3 seconds, write the next, and so on. But it won't even call the function.

<html>
<head></head>
<body>

<a href="http://www.google.com">Google</a>
<a href="http://www.thinkgeek.com">ThinkGeek</a>
<a href="http://www.themetapicture.com">The Meta Picture</a>

<iframe src="http://www.google.com" id="myid" name="main" width="1024" height="768">
</iframe>

<script>

function getLinksArray(){
for(var i=0; i < document.links.length; i++){
    var linx = document.links[i].href;
    setTimeout("openLinks(linx)"),3000);
}
}

function openLinks(link){
document.write(link + "<br />");
}

window.onload = getLinksArray();
</script>
</body>
</html>

The second part of my question is to change the src of the iframe to the links (instead of writing them). I'm only using the document.write for testing purposes to get the delay working.

I've tried document.getElementById("myid").src = link; and it won't do anything at all. Almost as if the iframe doesn't even exist.

I'm not a pro, so I'm here hoping to get help from a pro. ;) Thanks in advance.

Will
  • 301
  • 1
  • 7
  • 18
  • possible duplicate of [Javascript closure inside loops - simple practical example](http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example) – Bergi Feb 05 '13 at 19:43

4 Answers4

0

You're mostly having problems because you can't call setTimeout like that.

You should pass a function, which will need to be passed a bound variable, i.e.

for (var i=0; i < document.links.length; i++) {
    var linx = document.links[i].href;
    setTimeout(function(linx) {
        return function() {
            openlinks(linx);
        }
    }(linx),3000);
}

The inner stuff is one of the "standard" Javascript ways of passing a loop variable to a callback. If it doesn't make sense, you're gonna need a lot more help...

Alnitak
  • 334,560
  • 70
  • 407
  • 495
0

First, never, ever, pass a string to setTimeout.

// old and busted
setTimeout("openLinks(linx)"),3000);

// new hotness
setTimeout(function() {
  openLinks(linx)
}, 3000);

Second, loops that generate functions need some additional help. You need to capture the value of the loop in a closure, otherwise the value will change before the function that uses it executes.

function getLinksArray() {
    for (var i = 0; i < document.links.length; i++) {
        var linx = document.links[i].href;

        // create a closure for each loop iteration
        (function(linx) {
          setTimeout(function() {
            openLinks(linx);
          }, 3000);
        }(linx));

    }
}

function openLinks(link) {
    // document.write(link + "<br />");

    // document.write is also bad for a number of reasons
    someElement.innerHTML += link + "<br />"
}

window.onload = getLinksArray();

What that closure magic does is create brand new local variable shared only with the function created on each iteration of the loop. Without it you actually have something that looks more like this:

var linx; // var linx in the loop actually get hoisted to outside the loop!
for(var i=0; i < document.links.length; i++){
    linx = document.links[i].href;
    setTimeout(function() {
      openLinks(linx)
    },3000);
  }
}

Looking at that, you can see that the loop will be done running, linx will be set the last value of that loop, and then your first timeout will fire using the value of linx that all the timeouts share.

Creating and executing a function, passing in that value prevents that hoisting and sharing.

Alex Wayne
  • 178,991
  • 47
  • 309
  • 337
  • It does not do the exact same thing, since your function expression is defined in local scope while that string is `eval`ed in global context. Of course, the latter is not desired. – Bergi Feb 05 '13 at 19:48
  • @Bergi It does the same thing, without the suck. As for `document.write` yep, you are right. But you shouldn't be using that at all either. – Alex Wayne Feb 05 '13 at 19:52
  • But it's jumping straight to the last value in the array. I need it to load the first value into the iframe, wait 3 seconds, load the next and so on. Is this even possible? – Will Feb 05 '13 at 20:13
0

thanks to everyone's help I have a final working product:

<html>
<head></head>
<body>

<a href="http://www.google.com">Google</a><br />
<a href="http://www.thinkgeek.com">ThinkGeek</a><br />
<a href="http://www.themetapicture.com">The Meta Picture</a>

<iframe src="http://www.google.com" id="myid" name="main" width="1024" height="768">
</iframe>

<script>
function getLinksArray() {
for (var i = 0; i < document.links.length; i++) {
  var linx = document.links;
 (function loadLink(i) {          
   setTimeout(function () {   
    document.getElementById("myid").src = linx[i].href;
    if(linx[++i])
    {
        loadLink(i);
    }
}, 20000)
})(0);

}
}

window.onload = getLinksArray();
</script>
</body>
</html>
Will
  • 301
  • 1
  • 7
  • 18
0

To pause for 1 second in async function you can simply use:

await new Promise(_ => setTimeout(_, 1000));
Zuhair Taha
  • 2,808
  • 2
  • 35
  • 33