關於for循環裏面異步操做的問題 關於for循環裏面異步操做的問題

https://www.cnblogs.com/vipzhou/p/6519552.htmlhtml

 

關於for循環裏面異步操做的問題

 

首先來看一個比較簡單的問題,咱們想實現的就是每隔1s輸出0-4的值,就是這麼簡單,看下錯誤寫法:jquery

1
2
3
4
5
6
7
8
function test() {
     for  ( var  i = 0; i < 5; ++i) {
         setTimeout(function() {
             console.log( "index is :" , i);
         }, 1000);
     }
}
test();

以上代碼會如何輸出?輸出以下:數組

1
2
3
4
5
index  is  : 5
index  is  : 5
index  is  : 5
index  is  : 5
index  is  : 5

並且該操做幾乎是在同一時間完成,setTimeout定時根本就沒有起做用,這是由於:單線程的js在操做時,對於這種異步操做,會先進行一次「保存」,等到整個for循環執行結束後,此時i的值已經變成5,由於setTimeout是寫在for循環中的,至關於存在5次定時調用,這5次調用均是在for循環結束後進行的,因此天然而然輸出都是5,正確的實現有幾種,通常狀況下,咱們使用遞歸實現,以下:閉包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// var i = 0;
// var arr = [0, 1, 2, 3, 4];
 
// function box6() {
//     if (i < arr.length) {
//         setTimeout(function() {
//             console.log("index is : ", i);
//             i++;
//             box6();
//         }, 1000);
//     }
// }
 
box6();
 
function box7(param) {
     if  (param < 5) {
         console.log( "index is :" , param);
         setTimeout(function() {
             box7(param + 1);
         }, 1000)
     }
}
box7(0);

正確實現每隔1s打印輸出以下:異步

1
2
3
4
5
index  is  : 0
index  is  : 1
index  is  : 2
index  is  : 3
index  is  : 4

使用遞歸實現的倒計時:async

1
2
3
4
5
6
7
8
9
10
11
12
13
function showTime(count) {
     console.log( "count is : " , count);
     if  (count == 0) {
         console.log( "All is Done!" );
     else  {
         count -= 1;
         setTimeout(function() {
             showTime(count);
         }, 1000);
     }
}
 
showTime(20);

遞歸調用很好的解決了setTimeout同時執行的狀況,若是使用async、await實現,能夠以下寫法:函數

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var  asyncFunc = function(arr, i) {
     return  new  Promise(function(resolve, reject) {
         setTimeout(function() {
             arr.push(i);
             console.log( "index is : " , i);
             resolve();
         }, 1000);
     });
}
 
var  box5 = async function() {
     var  arr = [];
     for  ( var  i = 0; i < 5; i++) {
         await asyncFunc(arr, i);
     }
     console.log(arr);
}
 
box5();

一樣實現每隔1s正確地打印輸出以下:post

1
2
3
4
5
6
index  is  :  0
index  is  :  1
index  is  :  2
index  is  :  3
index  is  :  4
[ 0, 1, 2, 3, 4 ]

接下來再看個需求:構建一個函數數組,該數組每一項函數的功能是依次輸出0-4,錯誤寫法以下:測試

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function buildList(list) {
     var  result = [];
     for  ( var  i = 0; i < list.length; i++) {
         var  item =  'item'  + list[i];
         result.push(function() { console.log(item +  ' '  + list[i]) });
     }
     return  result;
}
 
function testList() {
     var  fnlist = buildList([1, 2, 3]);
     for  ( var  j = 0; j < fnlist.length; j++) {
         fnlist[j]();
     }
}
 
testList();

輸出以下:ui

1
2
3
item3 undefined
item3 undefined
item3 undefined

for循環裏面使用匿名函數和直接寫setTimeout調用比較相似,可是這裏又有點不一樣,for循環執行結束後,匿名函數開始調用,發現裏面存在「item」變量,這時依次會向上級查找,剛好找到循環結束時的item變量值爲「list[2]」即爲3,item爲3可是i的值已經變爲3,又由於list[3]的值爲undefined,因此這裏輸出3遍item3 undefined。不信能夠修改下數組以下,其他代碼不變:

1
2
3
4
5
6
7
8
9
10
function buildList(list) {
     ...
}
 
function testList() {
     var  fnlist = buildList([6, 7, 8]);
     ...
}
 
testList();

這裏絕對輸出的是:

1
2
3
item8 undefined
item8 undefined
item8 undefined

再來看下正確的實現:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function buildList(list) {
     var  result = [];
     for  ( var  i = 0; i < list.length; i++) {
         var  item =  'item'  + list[i];
         result.push(function(index, it) {
             return  function() {
                 console.log(it +  ' '  + list[index]);
             }
         }(i, item));
     }
     return  result;
}
 
function testList() {
     var  fnlist = buildList([6, 7, 8]);
     for  ( var  j = 0; j < fnlist.length; j++) {
         fnlist[j]();
     }
}
 
testList();

輸出以下:

1
2
3
item6 6
item7 7
item8 8

這裏主要使用的是即時執行函數,什麼是即時執行函數?能夠理解爲一個封閉的代碼塊,該代碼塊中的代碼會在定義時當即執行一遍,各個代碼塊的做用域彼此獨立,不會污染外部環境,寫法其實有不少種,上面只是一種,一樣的還有使用void、+、-、!等等,jquery源碼就是直接使用的這裏的圓括號寫法的這種。

再看幾個測試例子:

1
2
3
4
5
6
7
8
9
10
function box2() {
     var  arr = [];
     for  ( var  i = 0; i < 5; i++) {
         arr[i] = (function(num) {  //自我執行,並傳參(將匿名函數造成一個表達式)(傳遞一個參數)
             return  num;  //這裏的num寫什麼均可以                   
         })(i);  //這時候這個括號裏面的i和上面arr[i]的值是同樣的都是取自for循環裏面的i                           
     }
     return  arr;
}
console.log(box2());      //[ 0, 1, 2, 3, 4 ]
1
2
3
4
5
6
7
8
9
10
11
12
function box4() {
     var  arr = [];
     for  ( var  i = 0; i < 5; i++) {
         arr[i] = (function(num) {  //自我執行,並傳參(將匿名函數造成一個表達式)(傳遞一個參數),在這個閉包裏面再寫一個匿名函數
             return  function() {
                 return  num;
             }
         })(i);  //這時候這個括號裏面的i和上面arr[i]的值是同樣的都是取自for循環裏面的i                     
     }
     return  arr;
}
console.log(box4());    //[ [Function], [Function], [Function], [Function], [Function] ]

box4這種寫法其實跟上面有一種是一致的,就很少說了,其實主要就是閉包,稍微改變一下代碼,實現的結果卻大相徑庭,共勉吧。。。

相關文章
相關標籤/搜索