1. 程式人生 > 其它 >關於在for迴圈中使用setTimeout

關於在for迴圈中使用setTimeout

setTimeout的執行機制:

先將回調函式放到等待佇列中,等待區域內其他主程式執行完畢後,按時間順序先進先出執行回撥函式。本質上是作用域的問題。

在for迴圈中使用setTimeout時:

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

因setTimeout是非同步執行,每一次for迴圈的時候,setTimeout都執行一次,但是裡面的函式沒有被執行,而是被放到了任務佇列裡等待執行。只有主線上的任務執行完,才會執行任務佇列裡的任務。

即它會等到for迴圈全部執行完畢後,才會執行fun函式,當for迴圈結束後此時i的值已經變成了6,因此雖然定時器跑了5秒,控制檯上的內容依然是6。

另一種:

for (var i = 0; i < 5; i++) {
     (function() {
//       console.log(i);  //0 1 2 3 4 
//       console.log(i * 1000); //0 1000 2000 3000 4000
         setTimeout(function() { //在最後才執行,因為setTimeout是非同步載入
           console.log(i);// 5 5 5 5 5
         }, i * 1000);
     })(i);
// console.log(i); //0 1 2 3 4 } console.log(i)//5 **重點

由setTimeout的執行機制可以知道,首先會執行外部的所有主程式,雖然for迴圈內形成了閉包,但是fun並沒有發現一個實參所以跟第一個例子並無實際差別,仍然是連續輸出5個5。

解決方法:

1、使用閉包

 for (var i = 0; i < 5; i++) {
     (function(j) { 
         setTimeout(function() {
              console.log(j);// 0 1 2 3 4
         }, i * 1000);
      })(i);
// console.log(i); //0 1 2 3 4 }

依次輸出0到4,因為實際引數跟定時器內部的 i 有強依賴

通過閉包,將i的變數駐留在記憶體中,當輸出 j 時,引用的是外部函式的變數值i,i的值是根據迴圈來的,執行setTimeout時已經確定了裡面的的輸出了。

2、拆分結構

將setTimeout的的定義和呼叫放到不同的地方

function timer(i) {
    setTimeout( console.log( i ), i*1000 );// 1 2 3 4 5
}
for (var i=1; i<=5;i++) {
    timer(i);
}

 3、使用es6中的let

for(let i=1;i<=5;i++){
   setTimeout(functon(){
      console.log(i);  //1 2 3 4 5
   },i*1000) 
}

 因為for迴圈頭部的let不僅將 i 繫結到for迴圈中,事實上它將其重新繫結到迴圈體的每一次迭代中,確保上一次迭代結束的值被重新賦值。

setTimeout裡面的function()屬於一個新的域,通過var定義的變數是無法傳入到這個函式執行域中的,而使用let宣告塊變數能作用於這個塊 =》function就能使用 i 這個變量了

這個匿名函式的引數作用域和for引數的作用域不一樣,是利用了這一點來完成的。這個匿名函式的作用域有點類似類的屬性,是可以被內層方法使用的。

4、setTimeout第三個引數

for(var i= 1;i<=5;i++){
  setTimeout(function(i){
      console.log(i); // 1 2 3 4 5
  }, i*1000, i )
}

由於每次傳入的引數都是從for迴圈中取到的值,所以依次輸出1-5。

這當然還是作用域的問題,但是在這裡setTimeout第三個引數卻把i的值給儲存了下來。這種解決方法比使用閉包輕快的多。

擴充套件:

setTimeout(function(x){
  console.log(x)   // 1
},1000,1)

setTimeout(function(x,y){
  console.log(x+y)   // 3
},1000,1,2)

setTimeout的第三個引數起(可選引數)的作用就是第一個函式傳參。但是使用第三個引數需要注意的一點就是對IE9及以前的版本不相容。

 

轉載來源:https://www.cnblogs.com/wl0804/p/11987833.html