Passing functions to setTimeout in a loop: always the last value?

徘徊边缘 提交于 2019-12-16 22:52:10

问题


I'm trying to use setTimeout to execute an anonymous function that I pass information into and I'm having trouble. This (hard-coded version) would work just fine:

setTimeout(function(){alert("hello");},1000);
setTimeout(function(){alert("world");},2000);

But I'm trying to take the hello and world from an array and pass them into the function without (a) using global variables, and (2) using eval. I know how I could do it using globals or eval, but how can I do it without. Here is what I'd like to do (but I know it won't work):

var strings = [ "hello", "world" ];
var delay = 1000;
for(var i=0;i<strings.length;i++) {
    setTimeout( function(){alert(strings[i]);}, delay);
    delay += 1000;
}

Of course strings[i] will be out of context. How can I pass strings[i] into that anonymous function without eval or globals?


回答1:


This is the very frequently repeated "how do I use a loop variable in a closure" problem.

The canonical solution is to call a function which returns a function that's bound to the current value of the loop variable:

var strings = [ "hello", "world" ];
var delay = 1000;
for(var i=0;i<strings.length;i++) {
    setTimeout(
        (function(s) {
            return function() {
                alert(s);
            }
        })(strings[i]), delay);
    delay += 1000;
}

The outer definition function(s) { ... } creates a new scope where s is bound to the current value of the supplied parameter - i.e. strings[i] - where it's available to the inner scope.




回答2:


Just add a scope around the setTimeout call:

var strings = [ "hello", "world" ];
var delay = 1000;
for(var i=0;i<strings.length;i++) {
    (function(s){
        setTimeout( function(){alert(s);}, delay);
    })(strings[i]);
    delay += 1000;
}



回答3:


You could write a separate function to set up the timeout:

function doTimer(str, delay) {
  setTimeout(function() { alert(str); }, delay);
}

Then just call that from the loop:

var delay = 1000;
for(var i=0;i<strings.length;i++) {
    doTimer(strings[i], delay);
    delay += 1000;
}



回答4:


Although not as backward compatible as some of the other answers, thought I'd throw up another option.. this time using bind()!

var strings = [ "hello", "world" ];
var delay = 1000;
for(var i=0;i<strings.length;i++) {
    setTimeout(alert.bind(this, strings[i]), delay);
    delay += 1000;
}

View demo of it in action




回答5:


var strings = [ "hello", "world" ];
var delay = 1000;
for(var i=0;i<strings.length;i++) {
    setTimeout( new Function('alert(strings[i]);'), delay);
    delay += 1000;
}


来源:https://stackoverflow.com/questions/6425062/passing-functions-to-settimeout-in-a-loop-always-the-last-value

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!