淺談循環中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循環頭部的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