JS中For循環中嵌套setTimeout()方法的執行順序


在For循環中執行setTimeOut()方法的代碼,執行順序是怎樣的呢?

代碼如下

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

應該會有人會說,很簡單呀,for循環進行遍歷,並且每次有一個輸出,那結果應該是0,1,2,3,4。

其實不然,運行上訴代碼之后,控制台輸出如下:5個5

 

下面解釋下為什么是5個5.

首先關於最開始貼的代碼,我們是想讓計算機每循環一次的時候都會進入到setTimeOut()方法里執行console.log,輸出i之后再執行下一次循環。但是在JS里卻並不是這樣的。因為setTimeOut() 是一個異步函數,什么是異步函數呢?

首先我們都知道JS的執行機制是單線程環境。什么是單線程環境?打個比方,多線程就相當於一條公路上有多個車道,一次可以通過多輛車子。單線程就相當於這條公路就只有一個車道,每次只能通過一輛車。同理,在JS的單線程環境里,每次只能從上到下一條一條的把代碼執行下去。但是這樣一條一條按順序執行下去有的時候在面對特殊要求的時候速度太慢了。例如這條單車道的公路上現在要通行一輛救護車,救護車要是等到車道里的車子都通過了才能走那就太浪費時間了,不符合要求。那怎樣才能讓救護車以最快的速度通過這條單車道呢?這時候就引入了一個異步函數的概念。異步函數不是按正常代碼那樣要按順序等前面的代碼都執行完了才執行自己,當JS遇到異步函數的時候,會把異步函數插入到隊列中等待。也就是所謂的插隊。而setTimeOut 就是一個異步函數。所以當JS檢測到setTimeOut()的時候,會把setTimeOut()插入到隊列中,然后繼續執行后面的代碼,也就是接下來的循環。由於setTimeOut()設置了一秒后才執行,所以插入的隊列位置是一秒后。而在這個一秒內for循環已經全部完成,i經過五次循環后變成了5。所以當一秒后開始執行setTimeOut()方法的時候i的值已經變成5了。因為循環了5次,所以有5次setTimeOut()方法的調用,即輸出5個5。

用代碼表示的話就是我們最開始設想的流程是這樣的:

for(i=0) ——> console.log(0) ——> for(i=1) ——> console.log(1) ——> for(i=2) ——> console.log(2) ——> for(i=3) ——> console.log(3) ——> for(i=4) ——> console.log(4) ——> for(i=5) ——> 執行結束

但是在實際中的流程是這樣的:

for(i=0) ——> for(i=1) ——> for(i=2) for(i=3) ——> for(i=4) ——> for(i=5)ps:(這段循環都在一秒內完成了)——> console.log(5) ——> console.log(5) ——> console.log(5) ——> console.log(5) ——> console.log(5) ——> 執行完成

那么有什么辦法可以避免呢?

目前來看方法應該還是很多的,我目前知道的有三個,其他的方法有興趣可以自己再百度一下。

第一個方法的思路很簡單,因為setTimeOut()是異步執行,所以我們讓它立即執行就可以了。

for (var i = 0; i < 5; i++) { 
    (function (i) { 
        setTimeout(function () { 
      console.log(i);   },
1000 * i); })(i); //這里使用閉包 }

這段函數會讓JS檢測到setTimeOut時不再放到隊列中進行等待,而是立即運行setTimeOut()。所以能按我們所想的進行輸出。

第二個方法是使用let而不是var。即:

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

為什么let就行而var不行呢?因為let的作用域是塊作用域,所以每次JS檢測到setTimeOut把setTimeOut放到隊列的同時,let定義的i的值也會跟隨setTimeOut進去隊列。所以每次循環后隊列里的setTimeOut里的i的值是不一樣的。而var定義的i是無法進入setTimeOut的。i只能在運行到setTimeOut時才會向外層環境申請i的值,而這個時候i的值已經變成5了。

部分轉自:https://blog.csdn.net/qq_38054172/article/details/100764630


免責聲明!

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



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