You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript元组下Promise.all顺序执行函数的类型适配问题

实现顺序执行的Promise.all替代函数(带TypeScript类型支持)

我想实现一个类似Promise.all的函数,但它不会并行执行所有Promise,而是接收一个回调函数数组并按顺序执行。以下是我写的JavaScript实现:

function promiseAllSequential(promises) {
  return promises.reduce(
    (promiseAccumulator, currentPromise) =>
      promiseAccumulator.then((resultAccumulator) =>
        currentPromise.then((currentResult) => [...resultAccumulator, currentResult])
      ),
    Promise.resolve([])
  );
}

Promise.all的TypeScript类型签名如下:

all<T extends readonly unknown[] | []>(values: T): Promise<{ -readonly [P in keyof T]: Awaited<T[P]> }>;

为了让我的函数行为和它对齐,我设计了如下类型签名:

promiseAllSequential<FunctionsTuple extends readonly (() => unknown)[] | []>(
  functions: FunctionsTuple
): Promise<{
  -readonly [TupleIndex in keyof FunctionsTuple]: Awaited<ReturnType<FunctionsTuple[TupleIndex]>>;
}>

但我没法让这个类型和代码适配,核心问题是处理元组时如何给reduce函数添加正确的类型。之前我尝试用大量类型断言来解决错误,但想知道有没有不用断言的TypeScript特性可以实现这个需求。


解决方案:用递归类型推导替代reduce(或精简断言)

方案1:递归实现(类型推导更自然)

递归方式能让TypeScript自然跟踪元组每一步的类型,无需额外断言:

type SequentialResult<Fns extends readonly (() => unknown)[]> = {
  [K in keyof Fns]: Awaited<ReturnType<Fns[K]>>;
};

function promiseAllSequential<Fns extends readonly (() => unknown)[]>(
  functions: Fns
): Promise<SequentialResult<Fns>> {
  async function execute(index: number, results: unknown[]): Promise<SequentialResult<Fns>> {
    if (index >= functions.length) {
      return results as SequentialResult<Fns>;
    }
    const currentResult = await functions[index]();
    results.push(currentResult);
    return execute(index + 1, results);
  }

  return execute(0, []);
}

方案2:给reduce显式指定类型参数

如果坚持使用reduce,可以手动指定reduce的类型参数,让TypeScript识别累积器是对应元组的Promise,仅保留一次必要断言:

function promiseAllSequential<Fns extends readonly (() => unknown)[]>(
  functions: Fns
): Promise<{ [K in keyof Fns]: Awaited<ReturnType<Fns[K]>> }> {
  type ResultTuple = { [K in keyof Fns]: Awaited<ReturnType<Fns[K]>> };
  return functions.reduce(
    (acc, fn) => acc.then(async (results) => [...results, await fn()] as Partial<ResultTuple>),
    Promise.resolve([]) as Promise<Partial<ResultTuple>>
  ) as Promise<ResultTuple>;
}

这里仅在最后一步将Partial<ResultTuple>转为完整元组类型,因为我们能确定执行完所有函数后数组会填满所有元组项,相比大量临时断言更简洁。

内容的提问来源于stack exchange,提问作者user9540018

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 11:25:28