Promise.all已结算但用作Promise.all

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

我想为 Promise.allSettled 创建一些通用处理

目前 allSettled 返回已完成和已拒绝结果的列表。我不在乎,我真的不想到处处理它。 我想像 Promise.all 一样使用它 如果有任何错误,则拒绝,但要等到一切结束。这很重要。

private async allAndWait<T>(values: Iterable<PromiseLike<T>>): Promise<T[]> {
    const allSettled = await Promise.allSettled(values);
    const rejected = allSettled.find((promise) => promise.status === 'rejected') as PromiseRejectedResult | undefined;
    if (rejected) {
        throw rejected.reason;
    }
    return allSettled.map((promise) => (promise as PromiseFulfilledResult<T>).value);
}

创建了类似的东西,但它不允许我发送不同类型的承诺数组。而且返回类型只是第一种类型,而我想拥有和Promise.all一模一样的体验

typescript promise overloading variadic-tuple-types
1个回答
0
投票

总长:

async function allAndWait<T extends readonly unknown[] | []>(values: T & Iterable<PromiseLike<unknown>>): Promise<{-readonly [key in keyof T]: Awaited<T[key]>}>
async function allAndWait<T extends PromiseLike<unknown>>(values: Iterable<T>): Promise<Awaited<T>[]>
async function allAndWait<T extends readonly unknown[] | []>(values: Iterable<PromiseLike<unknown>>): Promise<any> {
  const allSettled = await Promise.allSettled(values) as PromiseSettledResult<Awaited<T[number]>>[]
  const rejected = allSettled.find((promise) => promise.status === 'rejected') as PromiseRejectedResult | undefined;
  if (rejected) {
      throw rejected.reason;
  }
  return allSettled.map((promise) => (promise as PromiseFulfilledResult<Awaited<T[number]>>).value)
}

这是我希望可以理解的过程:

我不知道为什么,但在你的代码中

T
没有被正确推断。我预计
T
是输入元组所具有的所有类型的联合(因此
string | number
)。我们可以通过将类型减速转移到通用减速来做到这一点。 (只需移动
PromiseLike<unknown>
就足够了)。

async function allAndWait<T extends Iterable<PromiseLike<unknown>>>(values: T) ...

这确实对我们没有多大帮助,因为我们需要 T 是一个元组而不是一个并集。正如您希望对返回类型进行排序(并非所有返回类型都是所有输入类型的并集类型)。我们是否以

[PromiseLike<string>, PromiseLike<number>]
[string, number]
的形式获得这样的元组并不重要,因为我们可以使用映射类型从前者中提取类型。所以假设我们有这样一个(PromiseLike)元组
T
我们可以简单地做:

{[key in keyof T]: Awaited<T[key]>}

我的第一直觉是使用休息运算符:

async function allAndWait<T extends Promise<unknown>[]>(...values: T): Promise<{[key in keyof T]: Awaited<T[key]>}> {...}

allAndWait(Promise.resolve("qwe"), Promise.resolve(2)) // typeof Promise<[string, number]>

请注意,我们必须将

Iteratable
放在数组上。因为我们不能在
Iteratable
上使用剩余运算符。请注意,
Iteratable
与元组类型不同。所以我们不能说第一个元素是字符串类型,第二个元素是数字类型,这是我们这里需要的。我们稍后可以对此用例进行重载(如果您确实需要它),并提供一个与联合一起使用的不太强大的类型实现。但首先我们需要解决你甚至不能传入数组的问题。你可以这样做:

const arr = [Promise.resolve("string"), Promise.resolve(2)]
allAndWait(...arr)

但这并不严格遵循您请求的类型实现。请注意,我们不能只从函数减速中省略剩余运算符,因为这会再次将元组转换为联合类型。所以我们必须应用一个技巧:

async function allAndWait<T extends readonly unknown[] | []>(values: T & Iterable<Promise<unknown>>): Promise<{-readonly [key in keyof T]: Awaited<T[key]>}> {...}

我们通过交集类型在这里需要两件事:首先,

values
必须是Iterable类型。其次,我们推断出一个非常通用的元组
T
,以便我们稍后可以将其用于导出。这行代码很大程度上受到
Promise.all
类型声明的启发,我不知道为什么在这里:
T extends readonly unknown[] | []
[]
的联合是必要的,但确实如此。

这只会为

Iteratable
s 留下不太强大的过载:

async function allAndWait<T extends PromiseLike<unknown>>(values: Iterable<T>): Promise<Awaited<T>[]>

我们必须将其放在另一个重载下方,因为它更通用并且符合所有可能的输入(因此永远不会达到另一个重载)。

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