flatMapでPromiseで返すのは意図した通りに動かない (javascript)

JavaScript
const arr = ['a', 'b', 'c']

// arr2 is string[]
const arr2 = arr.flatMap((item) => (item === 'a' ? [] : [item]))

// arr3 is Promise<string | never[]>[]
const arr3 = arr.flatMap(async (item) => (item === 'a' ? [] : item))

もっと具体的には条件に応じて非同期処理が入るような場合で、ついついこんな処理を書いてしまいそうですが、これは正しくありません。

const arr4 = arr.flatMap(async (item) => {
  if (item === 'a') {
    return []
  }

  await asyncFunc()

  return item
})

というのもこれはflatMapの戻り値がPromise<T>になるため、Promise<never[]> | Promise<T>となります。flatMapで空配列を戻せばunnestされて除去したことになるはずですが、Promise<never[]>が戻るため、unnestされません。

ですのでflatMap内に渡す関数は同期的な関数に留め、戻り値が[] | Promise<T>になるようにします。
例えば以下のような構造です。

const arr5 = arr.flatMap((item) => {
  if (item === 'a') {
    return []
  }
  return asyncFunc().then(() => item)
})

シンプルな場合はこれでもいいんですが、非同期関数を複数実行する必要がある時など、thenが連続して非常に読みにくい処理になるはずです。
いっそのことmap().filter()で書いちゃった方が実行時間的にも有利だし可読性高いと思う。

コメント