入夏的杭州, 出門熱浪撲面而來,敲程式碼睏意十足。馬路上一條條大白腿,琳琅滿目,參差不齊。沒錯,又到了減肥的好時候。(吹牛逼不能超過兩句)

進入本文的正題

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,儘量避免這種寫法,坑啊。。。