配列のmapやflatMapで非同期処理を扱うときは注意が必要
JavaScriptの配列のmapやflatMapの中で非同期処理を扱うときは注意が必要です。
例えば以下のような場合です。
const f = async a => a * 2;
const xs: Array<Promise<number>> = [1, 2, 3].map(f);
mapの中でPromise<number>を返す関数を扱った場合、戻り値がArray<Promise<number>>になります。でも欲しいのはPromise<Array<number>>です。そこでPromise<Array<number>>を返す関数mapAsyncを別途定義して目的を達成することにしました。
flatMapAsyncとmapAsyncの定義
flatMapAsync:
async function flatMapAsync<A, B>(
xs: Array<A>,
f: (a: A, i: number) => Promise<Array<B>>,
): Promise<Array<B>> {
const [ys, _] = await xs.reduce(async (o, a) => {
const [ps, i] = await o;
return [ps.concat(await f(a, i)), i + 1];
}, Promise.resolve<[Array<B>, number]>([[], 0]));
return ys;
}
mapAsync:
function mapAsync<A, B>(
xs: Array<A>,
f: (a: A, i: number) => Promise<B>,
): Promise<Array<B>> {
return flatMapAsync(xs, async (x, i) => [await f(x, i)]);
}
flatMapAsyncとmapAsyncの使用例
async function main() {
const xs = [1, 2, 3];
const a = await mapAsync(xs, async x => x * 2);
console.log(a); // [2, 3, 4]
const b = await flatMapAsync(xs, async x => [x, x + 10]);
console.log(b); // [1, 11, 2, 12, 3, 13]
}
main();
ここにも使用例があります。