为什么将 flatMap 与异步函数一起使用不返回展平数组?

问题描述 投票:0回答:2

我想了解为什么将

.flatMap()
async
一起使用不会返回展平数组。

例如,对于 Typescript,这会返回一个数字数组: 我知道

Promisse.all
async
对于简单的数字数组来说是没有用的,它只是更容易复制

const numbers = [[1], [2], [3]];
// typed as number[]
const numbersFlatten = await Promise.all(
    numbers.flatMap((number) => number),
);

对于 Typescript,返回一个数字数组的数组(刚刚添加了一个异步):

const numbers = [[1], [2], [3]];
// typed as number[][]
const numbersFlatten = await Promise.all(
    numbers.flatMap(async (number) => number),
);
javascript typescript flatmap
2个回答
9
投票

所有

async
函数都隐式返回 Promises。通过创建
.flatMap()
回调
async
,它现在返回一个解析为数组中的
number
的 Promise。为了让
.flatMap()
正常工作并展平其结果,回调应该返回一个数组,而不是 Promise。以下是有问题的行为的示例:

const numbers = [[1], [2], [3]];
const promiseAllArg = numbers.flatMap(async (number) => number); // same as `.flatMap(number => Promise.resolve(number))`, flatMap doesn't know how to flatten `Promise<number>` into a resulting array
console.log(promiseAllArg); // [Promise<[1]>, Promise<[2]>, Promise<[3]>]

相反,您可以使用常规

.map()
调用来获取解析值的嵌套数组,然后使用
.flat()
调用:

(async () => {
  const numbers = [[1], [2], [3]];
  const awaitedNumbers = await Promise.all(
    numbers.map(async (number) => number) // some async code
  );
  const numbersFlattened = awaitedNumbers.flat() // flatten the resolved values
  console.log(numbersFlattened);
})();


0
投票

我想补充一点,事实上

Promise
是 eager 也具有性能影响。

const asyncFlatMap = <A, B>(arr: A[], f: (a: A) => Promise<B>) =>
    Promise.all(arr.map(f)).then(arr => arr.flat());

asyncFlatMap([[1], [2], [3]], async nums => nums); // Promise<number[]>

您可以在上面的代码中看到,我们需要

map
,然后
flat
,由于中间存在
then
,因此需要通过微任务队列进行额外的往返。为了玩乐高而进行这种往返是浪费。

请注意,如果您不编写

asyncFlatMap
帮助程序,您当然可以避免
then
。我在这里做一个一般性的声明。

如果你碰巧嵌套了很多 Promise,你可能会对 Fluture 感兴趣。

期货是惰性的,所以你可以在任何东西运行之前用它们玩乐高。

import { resolve, parallel, promise, map } from 'fluture';
import { pipe } from 'fp-ts/function';

const numbersFlatten = await pipe(
    numbers.map(nums => resolve(nums)), // Future<number[]>[]
    parallel(10), // Future<number[][]>
    map(x => x.flat()), // Future<number[]>
    promise // Promise<number[]>
);

在上面的代码中,我使用

promise
将 Future 转换回 Promise,以匹配您的用例。只有当调用该函数时才会运行计算。
fork
也将在不涉及承诺的情况下运行计算。

如果您不熟悉

pipe
:第一个参数是通过其余函数参数传递的值。

10

中的神奇值
parallel
只是同时运行的最大计算数量。也可能是其他什么。

© www.soinside.com 2019 - 2024. All rights reserved.