Closed5

Array.prototype.flatMap と async/await の組み合わせが flatArray にならない

Ryotaro MorikatsuRyotaro Morikatsu

Prismaに渡すデータを作成中

const UserCreateInputs = users.flatMap(async (user) => {
  if (user.status.eq("DEACTIVATED")) return []
  const data = await fetchDataFromAnotherService(user.id)
  return [{
    id: data.uid,
    email: user.email,
  }]
})
// 期待
// { id: string; email: string }[]

// 結果
// Promise<{ id: string; email: string }[]>[]

こんな感じになってた。flatMap最強説だったんだけど、これは仕方ない。

Ryotaro MorikatsuRyotaro Morikatsu

対策案その1

const work1 = await Promise.all(UserCreateInputs)
// 結果
// { id: string; email: string }[][]

const work2 = work1.flat()
// 結果
// { id: string; email: string }[]

何してるかわかりづらそうなので、asyncFlatMapを素直に作ると良いかも

async function asyncFlatMap<T, U>(
  array: T[],
  callback: (item: T) => Promise<U>,
) {
  const promises = array.flatMap(callback);
  const nestedResults = await Promise.all(promises);
  return nestedResults.flat();
}

// test
const expected = await asyncFlatMap(users, async (user) => {
  if (user.status.eq("DEACTIVATED")) return []
  const data = await fetchDataFromAnotherService(user.id)
  return [{
    id: data.uid,
    email: user.email,
  }]
})
// 結果
// { id: string; email: string }[]
StathamStatham

ほぼ同じですが、自分はこう書いたのでなんとなく共有させてください

異なる点は

  1. promisesflatMap である必要はないので、ただの map にする
  2. callback の引数には flatMap の仕様通りに全て渡す
  3. arrow 関数で統一する
export const asyncFlatMap = async <Item, Res>(
  arr: Item[],
  callback: (value: Item, index: number, array: Item[]) => Promise<Res>
) => {
  const a = await Promise.all(arr.map(callback));
  return a.flat();
};

このスクラップは2022/01/25にクローズされました