Array.prototype.reduce
只能接受傳入 Synchronous Function,若要傳入 Asynchronous Function,可依需求使用不同方式實現。
Version
ECMAScript 2017
Synchronous reduce
let data = [1, 2, 3]
data.reduce ((ac, x) => {
return ac + x
}) // ?
reduce
會將 Array 計算成單一 value,可傳入 sync function,如預期回傳加總值。
Asynchronous reduce
await Last
let data = [1, 2, 3]
let sleep = ms => new Promise (resolve => setTimeout (resolve, ms))
await data.reduce (async (ac, x) => {
await sleep (1)
return (await ac) + x
}) // ?
若傳入 reduce
為 async function,此時 ac
亦成為 Promise,故也須搭配 await
解開 Promise。
await First
let data = [1, 2, 3]
let sleep = ms => new Promise (resolve => setTimeout (resolve, ms))
await data.reduce (async (ac, x) => {
await ac
await sleep (1)
return (await ac) + x
}) // ?
雖然都是 async function,但 await 寫法不同,在 timing 上會有些微差異:
- await first:一開始加上
await ac
,則 async function 將如同 sync function 依序執行,不受sleep
時間影響 - await last:
sleep
仍平行執行,會受sleep
時間影響
本例因為 Number 相加支援 associative law,因此 sleep
所造成的 timing 差異並不影響結果,若針對不支援 associative law 的 type 運算,就必須考慮 async function 本身的 timing 差異
for Loop + await
let data = [1, 2, 3]
let sleep = ms => new Promise (resolve => setTimeout (resolve, ms))
let result = 0
for (let x of data) {
await sleep (1)
result = result + x
}
result // ?
若覺得 reduce
處理 async function 很麻煩,也可簡單使用 for
loop + await
實現,此時相當於 reduce
+ await
first 寫法,並不受 sleep
時間影響。
Conclusion
- 在
reduce
內使用 async function 時,分await
last 與await
first 寫法,await
last 會受 timing 影響,而await
first 則不受 timing 影響 reduce
本來就不是設計用來使用 async function,若覺得reduce
處理 async function 很麻煩,可改用for
loop +await
較直覺,這也是少數for
loop 較適用場合,等效於await
first 寫法
Reference
Tamas Sallai, How to use async functions with Array.reduce in JavaScript