_ppzrw
Last Updated: June 27, 2017
·
27.67K
· zinkkrysty
B8f8f573e852c25d5750027fdb5ca38f

Be careful with setTimeout in loops

setTimeout is a great tool in JavaScript, but it has its drawbacks and problems you should be aware of:

There isn't a cross-browser way of passing a timeout callback with arguments. Only modern browsers support the following syntax:

setTimeout(myFunction, 1000, myVariable);

There are workarounds that look like this:

setTimeout(function(){
  myFunction(myVariable);
}, 1000);

but this leads to the following problem:

The variables you're passing in don't keep their initial values, so if you're changing them (as in a for loop) please be aware:

for (i = 1; i <= 5; ++i) {
  setTimeout(function(){
    console.log(i);
  }, 1000);
}

This will output the value 6 five times, which is not the intention.

Fortunately, there's a workaround for this

Set the timeout from within a function

for (i = 1; i <= 5; ++i) {
  setDelay(i);
}

function setDelay(i) {
  setTimeout(function(){
    console.log(i);
  }, 1000);
}

And there you have it! It's also a bit more elegant for larger projects.

Say Thanks
Respond

8 Responses
Add your response

6289
54e2ad6fd33097654a99a61818f61e0a
for (var i = 1; i <= 5; ++i) {
    (function(n) {
        setTimeout(function(){
            console.log(n);
        }, 1000);
    }(i));
}
over 1 year ago ·
6308
6d31b173ffdfbe37ae3c6f70afcd7a49

@herson I prefer your approach.

over 1 year ago ·
6314

@herson What's the diference between your code and cristian's code? It do the same thing!!

over 1 year ago ·
6456
54e2ad6fd33097654a99a61818f61e0a

@flipecoelho On the very last example, them both work the same, only that I use anonymous functions

over 1 year ago ·
6705
3f23a52c7bc87296e96b5a05547da0e0

Ran this through jsperf to get a quick comparison op-wise http://jsperf.com/anonymous-vs-named-settimeout-in-a-loop, anonymous is marginally quicker (although it might of been worth upping the number of loops) but the anonymous also has the edge on keeping the namespace tidy and, for me at least, is a slightly more elegant solution.

It's keeping the namespace tidy that gives it my nod.

over 1 year ago ·
9513
Img  uv6opa

Figuring out performance heuristics in JIT implementations is hard:
http://jsperf.com/anonymous-vs-named-settimeout-in-a-loop/2

(I'm a pythonista, so I prefer naming my functions anyways :P )

over 1 year ago ·
20833
20140507   stencil profile picture

I'd say you're half-right, but adding a proper closure and applying the context or arguments would be a bit cleaner.

Check out this SO post for details:
http://stackoverflow.com/questions/1728563/changing-the-scope-of-an-anonymous-function-on-a-settimeout-causes-a-weird-warni

over 1 year ago ·
29059

<html>
<head></head>
<body>
Using setTimeout in the example loop will not behave as expected, if you expect that there will be a one second interval between each task. What happens instead is the whole loop is executed in under a millisecond, and each of the 5 tasks is scheduled to be placed on the synchronous JS event queue one second later. They will run consecutively in order, but not each separated by a second.

The desired behavior can be realized by using so-called "recursive" syntax, e.g., by calling the next setTimeout from inside the cllback function itself. Actually it is not true recursive because the calling function returns before the new scheduled task runs, so the stack does not increase in size. This is important because you would never run out of stack space using the so-called "recursive" syntax.

I have created a
<a href="https://jsfiddle.net/craigphicks/702p020d/">short program on jsfiddle</a>
to demonstrate the above behaviors.
</body>
</html>

6 months ago ·