2011-08-28 53 views
1

應該很容易,但我的頭似乎剛剛被弄亂。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(); 
}); 

回答

3

這是一個很常見的問題,人們打了。

麻煩的是,你傳遞給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

1

帕特里克的解釋是相當不錯的。這裏有一些代碼可以幫助你解決它。

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的代理只是重置什麼「這」的功能指向..所以,你可以替換傳遞一個號碼,只是重置範圍:)(如果讓任何意義..長天)