メインコンテンツまでスキップ

JSの配列のmapとflatMapのPromiseの対応

· 約2分

配列のmapやflatMapで非同期処理を扱うときは注意が必要

JavaScriptの配列のmapflatMapの中で非同期処理を扱うときは注意が必要です。

例えば以下のような場合です。

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();

ここにも使用例があります。

async-array.spec.ts on GitHub