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 時,分awaitlast 與awaitfirst 寫法,awaitlast 會受 timing 影響,而awaitfirst 則不受 timing 影響 reduce本來就不是設計用來使用 async function,若覺得reduce處理 async function 很麻煩,可改用forloop +await較直覺,這也是少數forloop 較適用場合,等效於awaitfirst 寫法
Reference
Tamas Sallai, How to use async functions with Array.reduce in JavaScript