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上早有大神解釋了,可以參考這個鏈接

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

 

---------------------------------------------------------------------------------

 

你要每個定時器處理函數創建不同的“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)

“i”值乘以基礎延遲值,所以循環5次將導致分別延遲5秒,10秒,15秒,20秒,和25秒。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM