如何使用array.reduce返回Promise数组以实现异步并行执行?
问题描述
原本用array.map遍历数组返回Promise数组,现在要改用array.reduce实现,但直接替换后耗时暴涨近10倍——因为异步调用变成了串行执行。我想通过reduce生成一个可以后续用Promise.all()批量解析的Promise数组,以下是模拟场景和尝试过的方案,请指导正确实现方式:
interface IUser { name: string; balance: number | undefined; } let user1 = { name: 'user1', balance: undefined } let user2 = { name: 'user2', balance: undefined } let user3 = { name: 'user3', balance: undefined } const users = [user1, user2, user3] async function calculateBalance(user: IUser): Promise<IUser> { await new Promise((resolve) => setTimeout(resolve, 5000)); user.balance = Math.floor(Math.random() * 5000) + 1000; return user; } async function processUsers() { return users.reduce(async (acc, currUser, index) => { return [ ...acc, calculateBalance(currUser) ] // not working return acc.push(calculateBalance(currUser)) // Argument of type 'Promise<IUser>' is not assignable to parameter of type 'never' return acc.concat(calculateBalance(currUser)) // Same above }, []) } const processedUsersArray = await processUsers(); await Promise.all(processedUsersArray) // Desired thing for the sake of performance
问题根源
你犯的核心错误是给reduce的回调加了async,导致每一轮迭代的acc不再是普通数组,而是Promise对象。你试图对Promise用数组的展开、push、concat方法,自然会触发类型错误,同时还意外把异步调用变成了串行——因为每一轮都要等上一轮的Promise resolve才能继续。
正确实现
要达到和map一样的并行效果,reduce不需要异步回调,直接同步收集Promise即可。因为calculateBalance被调用时就会立即启动异步逻辑,我们只需要把这些已经在运行的Promise收集到数组里,最后用Promise.all等待全部完成就行:
interface IUser { name: string; balance: number | undefined; } let user1 = { name: 'user1', balance: undefined } let user2 = { name: 'user2', balance: undefined } let user3 = { name: 'user3', balance: undefined } const users = [user1, user2, user3] async function calculateBalance(user: IUser): Promise<IUser> { await new Promise((resolve) => setTimeout(resolve, 5000)); user.balance = Math.floor(Math.random() * 5000) + 1000; return user; } function processUsers() { // 去掉async,同步收集Promise return users.reduce((acc, currUser) => { acc.push(calculateBalance(currUser)); return acc; }, [] as Promise<IUser>[]); // 显式指定初始值类型,解决TS类型推断问题 } // 调用示例 (async () => { const promiseArray = processUsers(); const result = await Promise.all(promiseArray); console.log(result); // 5秒后全部返回,和map效果一致 })();
关键说明
- 并行逻辑的核心:
calculateBalance被调用时就会立即执行异步操作(启动5秒定时器),reduce只是把这些Promise收集起来,所有异步操作是同时开始的,总耗时还是5秒左右,不会串行。 - 类型处理:
[] as Promise<IUser>[]是为了让TypeScript正确识别初始数组的类型,避免类型报错。 - 为什么之前串行:加了
async的回调会把每一轮的返回值包装成Promise,reduce会等待上一轮的Promise resolve后才进入下一轮,导致异步操作一个接一个执行,总耗时变成5*3=15秒。
内容的提问来源于stack exchange,提问作者anileates
相关产品推荐
相关产品推荐

