js經典面試問題:如何讓for循環中的setTimeout()函數像預想中同樣工做?

setTimeout()是js中的一類重要函數,將一段代碼延遲必定時間並異步執行。可是這個函數常常不聽話。在實踐中,可能常常有人碰到相似下面的這種狀況:面試

for (var i = 1; i <= 2; i++) {
    setTimeout(function() { alert(i) }, 100);
}

 

咱們指望的結果是,先隔100毫秒彈出1,再隔100毫秒彈出2。可是跑起來後,alert的兩次內容都是數字3,並且緊挨着輸出,並非本身所指望的先1後2。有一種很基礎的面試題是,如何合理改動代碼,使它返回指望的結果?異步

其實很簡單。在stackoverflow上早有大神解釋了,能夠參考這個連接函數

答案翻譯成中文以下(並作了部分修改方便理解):oop

 

---------------------------------------------------------------------------------spa

 

你要每一個定時器處理函數建立不一樣的「i」變量副本。好比這樣:翻譯

function doSetTimeout(i) {
  setTimeout(function() { alert(i); }, 100);
}

for (var i = 1; i <= 2; ++i)
  doSetTimeout(i);
 
若是這樣事情 (這種方法在實際上也會其餘變種),每一個定時器處理函數就會共享同一做用域裏的同一變量"i"。循環完成的"i"是多少3!這裏經過定義一個函數來實現中介做用,從而建立變量副本因爲setTimeout()副本上下文中調用的,因此它每次都本身的私有的"i"以供使用

 

可是隨着時間的推移,這些代碼的效果顯得有些混亂是顯而易見的事實,由於設立一些時間間隔相同的連續的setTimeout()將致使全部延時處理程序同時被調用。瞭解設置timer(對setTimeout()的調用)幾乎不消耗時間是很重要的。也就是說,告訴系統「請在1000毫秒後調用此函數」將會被當即返回,由於在timer隊列中安裝延時請求的過程很是快。

 

所以,若是有一串連續的延時請求(好比我答案中的代碼),並且每個時間延遲值是相同的,那麼一旦通過足夠的時間全部延時處理程序一個一個快速連續調用

 

若是你須要的是在固定時間間隔調用的處理程序,你可使用setInterval(),行爲很是相似setTimeout(),但每通過必定時間就運行一次。或者你能夠調用以「時間乘以迭代計數器」爲時間間隔的setTimeout。也就是說,修改我剛纔的示例代碼:
 
function doScaledTimeout(i) {
  setTimeout(function() {
    alert(i);
  }, i * 5000);
}

 

(100毫秒超時,效果不會很明顯,因此我設置的數字高達5000)code

「i」值乘以基礎延遲值,因此循環5次將致使分別延遲5秒,10秒,15秒,20秒,和25秒。blog

相關文章
相關標籤/搜索