for循环和setTimeout的范围和时序问题。

for循环和setTimeout的范围和时序问题。

问题描述:

应该很容易,但我的头似乎刚刚被弄乱。for循环和setTimeout的范围和时序问题。

我的目标结果是0,1,2,0,1,2

我到目前为止的代码.... here是玩小提琴。

function delayedLoad(page){  

    console.log(page); 

}; 

function loadContent(){ 
    var i, 
     len = 3 

    for (i = 0; i < len; i++) { 

     console.log(i); 

     setTimeout(function(){ 
      delayedLoad(i); 
     },3000*i); 

    } 
}; 

$(document).ready(function(){ 
    // Load the content for the first time. 
    loadContent(); 
}); 

这是一个很常见的问题,人们打了。

麻烦的是,你传递给setTimeout在每次迭代函数引用相同i变量。

JavaScript没有块范围,只有函数范围。因此,要创建一个新的范围,将保留您想要的i值,您需要在循环内调用一个函数,并将其传递给i

例子:http://jsfiddle.net/GNwhR/3/

function set_up_timeout(j) { 
    setTimeout(function(){ 
     delayedLoad(j); 
    },3000*j); 
} 


function loadContent(){ 
    var i, 
     len = 3 

    for (i = 0; i < len; i++) { 

     console.log(i); 

     set_up_timeout(i); 

    } 
}; 

这会将你的电话setTimeout中被调用,并在每次迭代通过i另一个函数。这将创建一个新的变量范围,其中该函数中的j引用了正确的值。


另一种方法是让调用函数功能:

例子:http://jsfiddle.net/GNwhR/4/

function set_up_callback(j) { 
    return function(){ 
     delayedLoad(j); 
    }; 
} 


function loadContent(){ 
    var i, 
     len = 3 

    for (i = 0; i < len; i++) { 

     console.log(i); 

     setTimeout(set_up_callback(i), 3000*i); 

    } 
}; 

这一个电话set_up_callback,这回报一个函数引用j


最后,另一种常见的方法是使用一个IIFE (立即调用函数表达式)消除命名的函数。这是不太清楚IMO:

实施例:http://jsfiddle.net/GNwhR/5/

function loadContent(){ 
    var i, 
     len = 3 

    for (i = 0; i < len; i++) { 

     console.log(i); 

     setTimeout((function(j){ 
      return function() { 
       delayedLoad(j); 
      }; 
     })(i),3000*i); 

    } 
}; 

这是基本相同的,因为它返回一个函数前面的例子。主要区别在于返回函数的函数在循环内部创建并调用。

所有这些说明了同样的概念,你如果你希望以后异步引用它,并将它保持其预期值需要范围的变量。

+0

@帕特里克....这是一个非常好的解释。在我等待答案时,我设法解决了这个问题......就像我说的那样应该很简单......我真的不应该在周日晚上编写代码。 –

+0

@詹姆斯:啊,好的。很高兴你明白了。 – user113716

帕特里克的解释是相当不错的。这里有一些代码可以帮助你解决它。

function delayedLoad(page){  

    console.log(page); 

}; 

function loadContent(){ 
    var i, 
     len = 3  

    for (i = 0; i < len; i++) { 

     console.log('in loop', i); 

     setTimeout($.proxy(function(){ 
      delayedLoad(this.page); 
     }, {page: i}),3000*i); 

    } 
}; 

$(document).ready(function(){ 
    // Load the content for the first time. 
    loadContent(); 
}); 

jQuery的代理只是重置什么“这”的功能指向..所以,你可以替换传递一个号码,只是重置范围:)(如果让任何意义..长天)