淺談循環中setTimeout執行順序問題


 

淺談循環中setTimeout執行順序問題

(下面有見解一二)

 

期望:開始輸出一個0,然后每隔一秒依次輸出1,2,3,4。

 

for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, 1000 * i);
}

 結果:輸出5。

原因:setTimeout 使函數延遲1s執行,而for循環執行完成還不到0.1秒,到執行函數的時候,其實 i 已經變成5了,因此console.log(i)輸出5。

 

 

解決方法一:使用let塊作用域。 

for (let i = 0; i < 5; i++) { 
setTimeout(function() { 
console.log(i); 
}, 1000 * i); 
} 

 

 


解決方法二:加個閉包。

for (var i = 0; i < 5; i++) {
(function(i) {
setTimeout(function() {
console.log(i);
}, 1000 * i);
})(i);

結果:開始輸出一個0,然后每隔一秒依次輸出1,2,3,4。

 

失敗方法: 

for (var i = 0; i < 5; i++) {
(function() {
setTimeout(function() {
console.log(i);
}, 1000 * i);
})();
}

結果:輸出 5。

原因:去掉函數的參數i,則函數內部沒有對i保持引用。

 

 

解決方法三:

for (var i = 0; i < 5; i++) {
setTimeout((function(i) {
console.log(i);
})(i), i * 1000);
}

結果:立馬輸出0-4。

原因:setTimeout可以接受函數或者字符串作為參數,而給setTimeout傳遞了一個立即執行函數,該立即執行函數是undefined ,也就是說等價於setTimeout(undefined, ...)。立即執行函數會立即執行。

 

 

 

 

 見解一:

for(var i=0;i<5;i++){ 
    setTimeout(function(){ 
        console.log(i); 
 },1000) 

setTimeout是異步執行的,1000毫秒后向任務隊列里添加一個任務,只有主線上的全部執行完才會執行任務隊列里的任務,所以當主線程for循環執行完之后 i 的值為5,這個時候再去任務隊列中執行任務,i全部為5;

每次for循環的時候setTimeout都會執行,但是里面的function則不會執行被放入任務隊列,因此放了5次;for循環的5次執行完之后不到1000毫秒;

1000毫秒后全部執行任務隊列中的函數,所以就是輸出五個5啦

 

假如把var換成let,那么輸出結果為0,1,2,3,4;

因為let  i  的是區塊變量,每個i只能存活到大括號結束,並不會把后面的for循環的  i  值賦給前面的setTimeout中的i;而var i  則是局部變量,這個 i 的生命周期不受for循環的大括號限制;

 

 

 

見解二:

for(var i=0;i<=3;i++){

  setTimeout(function() {  

    console.log(i)  

  }, 10);

}

答案:打印4次4

 

這道題涉及了異步、作用域、閉包

settimeout是異步執行,10ms后往任務隊列里面添加一個任務,只有主線上的全部執行完,才會執行任務隊列里的任務,當主線執行完成后,i是4,所以此時再去執行任務隊列里的任務時,i全部是4了。對於打印4次是:

 每一次for循環的時候,settimeout都執行一次,但是里面的函數沒有被執行,而是被放到了任務隊列里面,等待執行,for循環了4次,就放了4次,當主線程執行完成后,才進入任務隊列里面執行。
   (注意:for循環從開始到結束的過程,需要維持幾微秒或幾毫秒。)
當我把var 變成let 時
for(let i=0;i<=3;i++){ setTimeout(function() {  console.log(i)  }, 10);}
打印出的是:0,1,2,3
當解決變量作用域,

因為for循環頭部的let不僅將i綁定到for循環快中,事實上它將其重新綁定到循環體的每一次迭代中,確保上一次迭代結束的值重新被賦值。setTimeout里面的function()屬於一個新的域,通過 var 定義的變量是無法傳入到這個函數執行域中的,通過使用 let 來聲明塊變量,這時候變量就能作用於這個塊,所以 function就能使用 i 這個變量了;這個匿名函數的參數作用域 和 for參數的作用域 不一樣,是利用了這一點來完成的。這個匿名函數的作用域有點類似類的屬性,是可以被內層方法使用的。

查了一下百度的一個答案:

setTimeout是一次執行函數,這里是10ms后執行,僅僅執行一次;for(var i=0;i<=3;i++),i的每次取值都是執行setTimeout這個函數,並沒有執行setTimeout里面的function(即閉包函數),setTimeout里面的function是有setTimeout的定時觸動的,也就是10ms后執行,也就是說i從0~3時,一共執行了4次的setTimeout()函數,此時的i的值是4,由於for語句的執行速度遠小於1秒,所以,1秒后,由setTimeout()函數定時觸動的閉包函數function()開始執行,alert(i);i的值已經是4了,所以相繼打印4次i.

 

 

 

 

 


---------------------
原文:https://blog.csdn.net/huakaiwuxing/article/details/78968642 


免責聲明!

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



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