最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to pass a variable into a setTimeout function? - Stack Overflow

programmeradmin3浏览0评论

I'm trying to set five staggered function calls (happening one second apart). That part works fine. What doesn't work is, I can't pass values 0 through 4 into the callback function. It just passes '5' each time. I can't seem to figure out why and how to fix it.

Code:

​function callback(num)
{
    console.log(num);
}

for (var i = 0, loadDelay = 1000; i < 5; ++ i, loadDelay += 1000)
    setTimeout(function() { callback(i); }, loadDelay);

Result:

5
5
5
5
5

Desired result:

0
1
2
3
4

I'm trying to set five staggered function calls (happening one second apart). That part works fine. What doesn't work is, I can't pass values 0 through 4 into the callback function. It just passes '5' each time. I can't seem to figure out why and how to fix it.

Code:

​function callback(num)
{
    console.log(num);
}

for (var i = 0, loadDelay = 1000; i < 5; ++ i, loadDelay += 1000)
    setTimeout(function() { callback(i); }, loadDelay);

Result:

5
5
5
5
5

Desired result:

0
1
2
3
4
Share Improve this question asked Apr 18, 2012 at 20:35 CaptSaltyJackCaptSaltyJack 16k17 gold badges72 silver badges101 bronze badges 1
  • increment the value in the callback. Since setTimeout is async, this is why you have always 5. – malletjo Commented Apr 18, 2012 at 20:37
Add a comment  | 

4 Answers 4

Reset to default 14

That's because you create a closure. So the function you pass to setTimeout share the same i instances. In the browser that supports the standards (not IE) you could have:

setTimeout(callback, loadDelay, i);

See: http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html#timers

Otherwise you have to actually bind the argument to the function:

setTimeout(callback.bind(undefined, i), loadDelay);

See: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind

If the browser doesn't support ES5 bind method, you can either implement the shim present in the link above, or manually doing something like:

setTimeout(function(index){
    return function() { callback(index) }
}(i), loadDelay);

But I would say it's more readable using bind and it's worthy to implement the shim. You can actually use this: https://github.com/kriskowal/es5-shim

To add es5 capabilities (where is possible) in the browser that don't support es5 natively.

Use a lambda / function expression to capture the current value. For example

for (var i = 0, loadDelay = 1000; i < 5; ++ i, loadDelay += 1000) {
  var doCall = function (j) {
    setTimeout(function() { callback(j); }, loadDelay);
  }
  doCall(i);
}

The problem here is that there is only 1 i value for all iterations of the loop. Variables in javascript have function scope even though you can declare them inside of a block. This means i is alive for the entire function.

To illustrate the problem consider the below code executes exactly the same as your sample

var i;
for (i = 0, loadDelay = 1000; i < 5; ++ i, loadDelay += 1000) {
  ...
}

My solution works because it introduces a new function and hence a new variable lifetime for j. This saves the current value of i in the function for use in the setTimeout callback

You needed a closure in order to pass i due to variable scoping. Check out this article, and this one as well for some good information on closures.

Live Demo

function callback(num)
{
    console.log(num);
}

for (var i = 0, loadDelay = 1000; i < 5; ++ i, loadDelay += 1000)
    setTimeout((function(num){return function(){
           callback(num);
        }
    })(i), loadDelay);​

setTimeout creates some odd scoping problems. Frame.js was designed to resolve some of this kind of confusion, this also works [updated]:

function callback(num) {
    console.log(num);
}

for (var i = 0, loadDelay = 1000; i < 5; ++ i, loadDelay += 1000) {
    Frame(function(next, i){
        setTimeout(function() { callback(i); }, loadDelay);
        next();
    }, i);
}
Frame.init();
发布评论

评论列表(0)

  1. 暂无评论