入夏的杭州, 出門熱浪撲面而來,敲程式碼睏意十足。馬路上一條條大白腿,琳琅滿目,參差不齊。沒錯,又到了減肥的好時候。(吹牛逼不能超過兩句)
進入本文的正題
ES7
引入了
async
、
await
方法,來處理非同步操作。關於這倆方法的使用,不再贅述,網上的文章多如牛毛,又寫一篇入門,未免廢話連篇。今天要分享的內容是在js的一些迴圈方法中,
async
和
await
的表現是如何的。
for迴圈
在for迴圈中,
async
和
await
表現的還是令人滿意。現在假設一個場景,設定一個變數,爸媽我三個人的體重,求和
const
family
=
{
“dad”
:
150
,
“mom”
:
100
,
“son”
:
200
}
const
familyToGet
=
[
“dad”
,
“mom”
,
“son”
]
// 寫一個sleep方法
const
sleep
=
ms
=>
{
return
new
Promise
(
resolve
=>
setTimeout
(
resolve
,
ms
))
}
// 假設獲取體重資料的過程是一個請求介面的非同步過程
const
getFamilyWeight
=
person
=>
{
return
sleep
(
2000
)。
then
(()
=>
family
[
person
])
}
const
loop_for
=
async
()
=>
{
console
。
log
(
‘start’
)
let
result
=
0
for
(
let
i
=
0
;
i
<
familyToGet
。
length
;
i
++
)
{
const
person
=
familyToGet
[
i
]
const
weight
=
await
getFamilyWeight
(
person
)
result
+=
weight
}
console
。
log
(
‘result:’
,
result
)
console
。
log
(
‘end’
)
}
loop_for
()
// 執行結果
// start
// 150
// 100
// 200
// result: 450
// end
按照上述程式碼的列印結果分析,for迴圈內的程式碼,每一次都會等當前的 getFamilyWeight 方法返回了內容之後,才會繼續下一個迴圈,迴圈全部結束的時候,才會跑for迴圈下面的程式碼,這是我們想要的結果,nice。
forEach迴圈
在
forEach
中,
async
和
await
表現的差強人意,具體原因,還應歸結於
forEach
中的回撥函式,具體看看程式碼的表現
const
family
=
{
“dad”
:
150
,
“mom”
:
100
,
“son”
:
200
}
const
familyToGet
=
[
“dad”
,
“mom”
,
“son”
]
// 寫一個sleep方法
const
sleep
=
ms
=>
{
return
new
Promise
(
resolve
=>
setTimeout
(
resolve
,
ms
))
}
// 假設獲取體重資料的過程是一個請求介面的非同步過程
const
getFamilyWeight
=
person
=>
{
return
sleep
(
2000
)。
then
(()
=>
family
[
person
])
}
const
loop_forEach
=
()
=>
{
console
。
log
(
‘start’
)
let
result
=
0
// 在回撥函式中,非同步是不好控制的
familyToGet
。
forEach
(
async
person
=>
{
const
num
=
await
getFamilyWeight
(
person
)
console
。
log
(
num
)
result
+=
num
})
console
。
log
(
‘result’
,
result
)
console
。
log
(
‘end’
)
}
loop_forEach
()
// 執行結果
// start
// result 0
// end
// 150
// 100
// 200
從執行結果中,可以看出,程式碼沒有等待
forEach
方法執行結束,而是直接繼續往後執行了。回撥函式傳進去之後,2秒鐘之後才會返回資料,這不是我們想要的結果,我現在是希望它能順序的執行。
那麼我們分析一下
forEach
內部大致的原理
Array
。
prototype
。
forEach
=
function
(
cb
)
{
// this 為當前呼叫該函式的變數
for
(
let
i
=
0
;
i
<
this
。
length
;
i
++
)
{
cb
(
this
[
i
],
i
);
}
}
且看上面的簡易
forEach
重寫,其實內部也是一個for迴圈,那麼為什麼不能表現的和for迴圈一樣的執行結果呢?
我們的回撥函式進入
forEach
之後,又被單獨的以
cb()
這種形式執行了一次,相當於在內部又建立了一個
async
await
形式的方法,當
forEach
函式執行完的時候,相當於建立了3個方法,這3個方法需要等待2秒鐘,資料才會返回,而forEach外面的程式碼是不會等這3個函式返回內容再執行,而是一意孤行的管自己走了,所以才會造成這樣的局面。返回問題的根本,我現在的需求是想要程式碼能順序執行,且最後能算出一家人體重之和,下面我們來修改一下程式碼
const
family
=
{
“dad”
:
150
,
“mom”
:
100
,
“son”
:
200
}
const
familyToGet
=
[
“dad”
,
“mom”
,
“son”
]
// 寫一個sleep方法
const
sleep
=
ms
=>
{
return
new
Promise
(
resolve
=>
setTimeout
(
resolve
,
ms
))
}
// 假設獲取體重資料的過程是一個請求介面的非同步過程
const
getFamilyWeight
=
person
=>
{
return
sleep
(
2000
)。
then
(()
=>
family
[
person
])
}
const
loop_forEach
=
async
()
=>
{
console
。
log
(
‘start’
)
let
promise
=
[]
// 在回撥函式中,非同步是不好控制的
familyToGet
。
forEach
(
person
=>
{
const
num
=
getFamilyWeight
(
person
)
promise
。
push
(
num
)
})
const
result
=
await
Promise
。
all
(
promise
)
console
。
log
(
‘result’
,
result
)
const
weight
=
result
。
reduce
((
sum
,
personWeight
)
=>
sum
+
personWeight
)
console
。
log
(
‘weight’
,
weight
)
console
。
log
(
‘end’
)
}
loop_forEach
()
// 執行結果
// start
// result [150, 100, 200]
// weight 450
// end
在
forEach
的回撥函式內,直接把
getFamilyWeight
方法返回的
promise
物件push到
promise
這個陣列變數內,透過
Promise。all
來做一層等待的非同步處理。
map遍歷
在
map
中,直接返回一個
promise
陣列
const
family
=
{
“dad”
:
150
,
“mom”
:
100
,
“son”
:
200
}
const
familyToGet
=
[
“dad”
,
“mom”
,
“son”
]
// 寫一個sleep方法
const
sleep
=
ms
=>
{
return
new
Promise
(
resolve
=>
setTimeout
(
resolve
,
ms
))
}
// 假設獲取體重資料的過程是一個請求介面的非同步過程
const
getFamilyWeight
=
person
=>
{
return
sleep
(
2000
)。
then
(()
=>
family
[
person
])
}
const
loop_map
=
async
()
=>
{
console
。
log
(
‘start’
)
// map中返回的是一個promise陣列,需要透過Promise。all處理
const
promise
=
familyToGet
。
map
(
async
person
=>
{
const
num
=
await
getFamilyWeight
(
person
)
return
num
;
})
console
。
log
(
‘promise’
,
promise
)
// 等待promise裡的內容全部返回,才會繼續往下執行
const
result
=
await
Promise
。
all
(
promise
)
console
。
log
(
‘result’
,
result
)
const
weight
=
result
。
reduce
((
sum
,
personWeight
)
=>
sum
+
personWeight
)
console
。
log
(
‘weight’
,
weight
)
console
。
log
(
‘end’
)
}
loop_map
()
// 執行結果
// start
// promise [Promise, Promise, Promise]
// result [150, 100, 200]
// weight 450
// end
大家應該能感覺到其實這個和上面改版後的
forEach
差不多,沒錯,正如你所料,只不過
map
方法能返回新的陣列罷了,
forEach
則不能返回陣列。
在這裡把
map
的簡單實現寫一下,方便理解
Array
。
prototype
。
map
=
function
(
cb
)
{
// this 為當前呼叫該函式的變數
var
o
=
[];
for
(
let
i
=
0
;
i
<
this
。
length
;
i
++
)
{
var
temp
=
cb
(
this
[
i
],
i
);
o
。
push
(
temp
);
}
return
o
;
}
如上述程式碼,在外面透過
o
這個變數收集
cb()
回撥函式的返回值,再到外面統一處理。真是妙啊,大家可以細細品味一番,能吾出很多新的物件。
總結
1、for迴圈內使用async和await還是可以的,穩如老狗
2、不要在forEach方法內使用async、await,儘量避免這種寫法,坑啊。。。