如何在async/await函数中应用DRY原则且避免过度性能开销?
优化异步重复代码提取后的性能问题
问题背景
有两个包含大量重复异步逻辑的函数,提取共享函数后,getAllMessageStats的执行时间暴涨至原来的3倍以上,尤其在消息量上千时表现明显。核心问题是提取共享函数后,串行的await导致异步请求无法并行处理,但又不想保留重复代码,寻求优雅的解决方案。
原函数实现
函数1:getAllMessageStats
const getAllMessageStats = async (userId: string) => { const messageIds = await getUserMessageIds(userId); let messagesRead = 0; let reactionsTotal = 0; const moreStats = []; for (const messageId of messageIds) { const message = await getMessage(messageId); if (message.read) messagesRead++; reactionsTotal += message.reactions; const otherStats = []; for (const otherDataId of message.otherDataIds) { const otherData = await getOtherData(otherDataId) otherStats.push(doLotsOfOtherCalcs(otherData)); } moreStats.push(otherStats); } return { messagesRead, reactionsTotal, moreStats } };
函数2:getMessageStats
const getMessageStats = async (messageId: string) => { const message = await getMessage(messageId); const otherStats = []; for (const otherDataId of message.otherDataIds) { const otherData = await getOtherData(otherDataId) otherStats.push(doLotsOfOtherCalcs(otherData)); } const miscData = await getMiscData(messageId); return { read: message.read, reactions: message.reactions, otherStats, miscData } };
尝试的共享函数实现(性能暴跌)
const sharedFunction = async (messageId: string) => { const message = await getMessage(messageId); const otherStats = []; for (const otherDataId of message.otherDataIds) { const otherData = await getOtherData(otherDataId) otherStats.push(doLotsOfOtherCalcs(otherData)); } return { read: message.read, reactions: message.reactions, otherStats } }; const getMessageStats = async (messageId: string) => { const { read, reactions, otherStats } = await sharedFunction(messageId); const miscData = await getMiscData(messageId); return { read, reactions, otherStats, miscData } }; const getAllMessageStats = async (userId: string) => { const messageIds = await getUserMessageIds(userId); let messagesRead = 0; let reactionsTotal = 0; const moreStats = []; for (const messageId of messageIds) { const { read, reactions, otherStats } = await sharedFunction(messageId); if (read) messagesRead++; reactionsTotal += reactions; moreStats.push(otherStats); } return { messagesRead, reactionsTotal, moreStats } };
解决方案
性能暴跌的核心原因是串行执行异步请求:原函数中虽然也是串行,但提取共享函数后,每个sharedFunction内部的getMessage和getOtherData都是串行,且外层循环也是逐个await,完全没有并行空间。可以通过以下两种方式优化,同时保留代码复用性:
1. 并行化批量处理请求
将循环中的串行await改为Promise.all,同时处理多个异步请求,大幅减少等待时间。可以拆分共享逻辑为“单消息处理逻辑”,然后在批量场景下并行调用:
// 保留共享的单消息处理逻辑 const processSingleMessage = async (messageId: string) => { const message = await getMessage(messageId); // 并行获取所有otherData,而非串行 const otherDataList = await Promise.all( message.otherDataIds.map(id => getOtherData(id)) ); const otherStats = otherDataList.map(doLotsOfOtherCalcs); return { read: message.read, reactions: message.reactions, otherStats }; }; // 优化getMessageStats const getMessageStats = async (messageId: string) => { const messageStats = await processSingleMessage(messageId); const miscData = await getMiscData(messageId); return { ...messageStats, miscData }; }; // 优化getAllMessageStats:并行处理所有消息 const getAllMessageStats = async (userId: string) => { const messageIds = await getUserMessageIds(userId); // 并行处理所有消息,而非逐个await const messageStatsList = await Promise.all( messageIds.map(id => processSingleMessage(id)) ); // 同步聚合统计数据 const { messagesRead, reactionsTotal, moreStats } = messageStatsList.reduce( (acc, stats) => { if (stats.read) acc.messagesRead++; acc.reactionsTotal += stats.reactions; acc.moreStats.push(stats.otherStats); return acc; }, { messagesRead: 0, reactionsTotal: 0, moreStats: [] } ); return { messagesRead, reactionsTotal, moreStats }; };
2. 进一步优化:批量获取数据(如果后端支持)
如果后端提供批量接口(比如getMessagesByIds、getOtherDataByIds),可以进一步减少请求次数,这是性能优化的终极方案:
// 共享的统计计算逻辑(同步函数,纯计算) const calculateMessageStats = (message: Message, otherDataList: OtherData[]) => { const otherStats = otherDataList.map(doLotsOfOtherCalcs); return { read: message.read, reactions: message.reactions, otherStats }; }; // 优化getAllMessageStats:批量获取所有数据 const getAllMessageStats = async (userId: string) => { const messageIds = await getUserMessageIds(userId); // 批量获取所有消息 const messages = await getMessagesByIds(messageIds); // 收集所有需要的otherDataId const allOtherDataIds = messages.flatMap(msg => msg.otherDataIds); // 批量获取所有otherData const otherDataMap = new Map( (await getOtherDataByIds(allOtherDataIds)).map(data => [data.id, data]) ); // 同步计算每个消息的统计 const messageStatsList = messages.map(message => { const otherDataList = message.otherDataIds.map(id => otherDataMap.get(id)!); return calculateMessageStats(message, otherDataList); }); // 聚合结果 return messageStatsList.reduce( (acc, stats) => { if (stats.read) acc.messagesRead++; acc.reactionsTotal += stats.reactions; acc.moreStats.push(stats.otherStats); return acc; }, { messagesRead: 0, reactionsTotal: 0, moreStats: [] } ); }; // 优化getMessageStats:复用计算逻辑 const getMessageStats = async (messageId: string) => { const message = await getMessage(messageId); const otherDataList = await Promise.all( message.otherDataIds.map(id => getOtherData(id)) ); const messageStats = calculateMessageStats(message, otherDataList); const miscData = await getMiscData(messageId); return { ...messageStats, miscData }; };
关键优化点
- 并行请求:用
Promise.all替代串行await,同时发起多个异步请求,减少总等待时间。 - 拆分逻辑:将异步数据获取和同步计算分离,共享纯计算逻辑,避免异步逻辑的重复。
- 批量请求:如果后端支持,尽量使用批量接口减少网络请求次数,这对性能提升最显著。
内容的提问来源于stack exchange,提问作者eltydari
相关产品推荐
相关产品推荐

