點燈坊

失くすものさえない今が強くなるチャンスよ

在 filter 內使用 Asynchronous Function

Sam Xiao's Avatar 2021-08-01

Array.prototype.filter 只能接受傳入 Synchronous Function,若要傳入 Asynchronous Function,可依需求使用不同方式實現。

Version

ECMAScript 2017

Synchronous filter

let data = [1, 2, 3]

let pred = x => x === 1

data.filter (pred) // ?

filter 會根據傳入 predicate 過濾,可傳入 sync function,如預期回傳新 Array。

filter000

Asynchronous filter

let data = [1, 2, 3]

let pred = async x => x === 1

data.filter (pred) // ?

若對 filter 傳入 async function,雖然沒有錯誤,但結果完全不對,因為 filter 並不支援 async function。

filter001

Promise.all + map

let data = [1, 2, 3]

let pred = async x => x === 1

let result = await Promise.all (data.map (pred))

data.filter ((x, i) => result [i]) // ?

使用 Promise.all + Array.prototype.map 實現 async filter

Array.prototype.map 搭配 Promise.all 則可接受 async function,藉由此特性先將 async function 所有結果先求得,再搭配 filter 以 lookup table 方式回傳 value。

filter003

Promise Chain

let data = [1, 2, 3]

let pred = async x => x === 1

await Promise
  .all (data.map (pred))
  .then (x => data.filter ((_, i) => x [i])) // ?

也可改用 Promise Chain 方式實現 async filter

filter004

for Loop + await

let data = [1, 2, 3]

let pred = async x => x === 1

let result = []

for (let x of data) {
  if (await pred (x)) 
    result.push (x)
}
    
result // ?

若覺得 Promise.all + map 組合較麻煩,也可簡單使用 for loop + await 實現。

filter002

Conclusion

  • 雖然 filter 無法接受 async function,但由於 filter 可透過 map 實現,因此可藉由 Promise.all + map 實現 async filter
  • filter 本來就不是設計用來使用 async function,若覺得還要搭配 Promise.all 太麻煩,可改用 for loop + await 較直覺,這也是少數 for loop 較適用場合

Reference

Tamas Sallai, How to Use Async Functions with Array.filter in JavaScript ?