无法在setTimeout中正确访问数组元素

时间:2012-11-28 15:00:10

标签: javascript asynchronous

这是我想要做的。 我有一个类似下面的数组

var my_array = ['1', '2', '3' ... ,'1000000000000000'];

我想要做的是为该数组的每个元素创建一堆HTML元素,并且因为该数组可以包含大量元素,所以我尝试执行以下操作,因此浏览器不会冻结。

for(var i in my_array)
{
    if(my_array.hasOwnProperty(i))
    {
       setTimeout(function(){
             do_something_with_data(my_array[i]);
       });
    }
 }

虽然setTimeout中的my_array [i]没有应有的值,但会发生什么。

为了更准确,当我尝试console.log(my_array[i])我得到的东西是这样的:

"getUnique" function (){
   var u = {}, a = [];
   for(var i = 0, l = this.length; i < l; ++i){
      if(u.hasOwnProperty(this[i])) {
         continue;
      }
      a.push(this[i]);
      u[this[i]] = 1;
   }
   return a;
}

getUnique是我添加到Array原型中的函数,如下所示:

Array.prototype.getUnique = function(){
   var u = {}, a = [];
   for(var i = 0, l = this.length; i < l; ++i){
      if(u.hasOwnProperty(this[i])) {
         continue;
      }
      a.push(this[i]);
      u[this[i]] = 1;
   }
   return a;
};

有人可以帮我解决这个问题吗?

3 个答案:

答案 0 :(得分:3)

在循环完成后执行setTimeout,i是该点的最后一个键或一些垃圾值。你可以这样捕捉我:

for (var i in my_array) {
    if (my_array.hasOwnProperty(i)) {
        (function(capturedI) {
            setTimeout(function() {
                do_something_with_data(my_array[capturedI]);
            });
        })(i);
    }
}

你也不应该对数组使用for..in循环,因为它比for循环慢一个数量级(尤其是使用.hasOwnProperty检查),并且没有定义迭代顺序

如果您有jQuery或愿意为旧浏览器添加一些额外的代码,您可以这样做:

my_array.forEach( function( item ) {
     setTimeout( function() {
         do_something_with_data( item );
     }, 1000);
});

使用jQuery:

$.each( my_array, function( index, item ) {
     setTimeout( function() {
         do_something_with_data( item );
     }, 1000);
});

查看[].forEach

的文档

答案 1 :(得分:2)

问题是您创建的函数对i变量有一个引用,而不是的副本,所以当它们出现时他们看到i就像在那个时间点那样(大概是在数组的末尾)。 (更多:Closures are not complicated

我建议使用完全不同的方法(下面),但首先,让我们看看如何使现有方法有效。

要执行您尝试执行的操作,使用for循环,您必须使函数关闭不会更改的内容。通常的方法是使用工厂函数创建超时函数,以便它们关闭工厂的参数。或者实际上,您可以传入数组元素的值而不是索引变量。

for(var i in my_array)
{
    if(my_array.hasOwnProperty(i))
    {
       setTimeout(makeFunction(my_array[i]));
    }
 }

function makeFunction(entry) {
    return function(){
        do_something_with_data(entry);
    };
}

但是,我可能会对代码进行重组,因此您不必不必要地创建大量的函数对象。相反,使用一个函数,让它接近一个递增的索引:

// Assumes `my_array` exists at this point, and that it
// has at least one entry
var i = 0;
setTimeout(tick, 0);
function tick() {
    // Process this entry
    if (my_array.hasOwnProperty(i)) {
        do_something_with_data(my_array[i]);
    }

    // Move to next
    ++i;

    // If there are any left, schedule the next tick
    if (i < my_array.length) {
        setTimeout(tick, 0);
    }
}

答案 2 :(得分:0)

这只是猜测。试试吧:

for(var i in my_array)
{
    if(my_array.hasOwnProperty(i))
       setTimeout("do_something_with_data('"+my_array[i]+"')", 500);
}