当前位置: 首页 > article >正文

基于Promise链式调用的多层级请求性能优化

代码优化-循环嵌套关联请求

1. 背景

在实际开发中,我们经常会遇到需要嵌套关联请求的场景,比如:

  • 获取项目列表
  • 获取项目详情
  • 获取项目进度

2. 问题

在这种场景下,我们可能会遇到以下问题:

  1. 串行请求瀑布流:for循环内的await导致接口调用时间叠加
  2. 状态依赖耦合:进度请求依赖前序请求的状态结果
  3. 数据合并冗余:多次对象赋值操作影响性能
  4. 错误处理缺失:任意请求失败会导致整个流程中断
  5. 状态映射重复:每次处理状态都进行数组查找
    async loadProjectData() {this.loading = true;try {const res = await this.$api.projectService.AA({ clientId: this.customer.clientId });if (res.code !== 0 || res.data?.length < 1) return;let projectList = res.data;for (let i = 0; i < projectList.length; i++) {const projectInfoRes = await this.$api.projectService.BB({ projectCode: res.data[i].projectCode });if (projectInfoRes.code === 0 && projectInfoRes.data && projectInfoRes.data?.records.length > 0) {projectList[i] = { ...projectList[i], ...projectInfoRes.data.records[0] };if (projectList[i].projectStatus) {projectList[i].statusLabel = PROJECT_STATUS_MAP.find(item => item.value === projectList[i].projectStatus)?.label || projectList[i].projectStatus;projectList[i].statusColor = PROJECT_STATUS_MAP.find(item => item.value === projectList[i].projectStatus)?.color || '';const projectRes = await this.$api.projectService.CC({ projectCode: res.data[i].projectCode, projectStatus: projectList[i]?.projectStatus });if (projectRes.code === 0) {projectList[i].info = projectRes.data;}}}}this.projectList = projectList;} catch (error) {console.log(error);}this.loading = false;}

3. 解决方案

3.1 第一阶段:职责分离与并行优化(方案一)

优化目标

  • 解耦请求依赖
  • 提升并行度
  • 统一错误处理
  1. 将progress请求改为使用details中的状态参数
  2. 在数据合并阶段提前处理状态信息
  3. 保持每个方法的单一职责:
    • fetchProjectProgress:专注处理带参数的API请求
    • mergeProjectData:负责数据合并和状态处理调度
    • processProjectStatus:专注状态转换逻辑
  4. 保持并行处理项目级别的请求,串行处理单个项目的依赖请求
  5. 使用更清晰的函数命名提升代码可读性
  6. 移除不必要的 console.log 改用 console.error 处理错误
    async loadProjectData() {this.loading = true;try {const baseList = await this.fetchBaseProjects();if (!baseList) return;// 并行处理const processedList = await Promise.all(baseList.map(async (item) => {const details = await this.fetchProjectDetails(item.projectCode);const progress = await this.fetchProjectProgress(item.projectCode,details?.projectStatus);return this.mergeProjectData(item, details, progress);}));this.projectList = processedList;} catch (error) {console.error('项目加载失败:', error);} finally {this.loading = false;}},async fetchBaseProjects() {const res = await this.$api.projectService.AA({ clientId: this.customer.clientId });return res.code === 0 && res.data?.length ? res.data : null;},async fetchProjectDetails(projectCode) {const res = await this.$api.projectService.BB({ projectCode });return res.code === 0 && res.data?.records.length ? res.data.records[0] : null;},async fetchProjectProgress(projectCode, projectStatus) {const res = await this.$api.projectService.CC({ projectCode, projectStatus });return res.code === 0 ? res.data : null;},// 数据合并处理mergeProjectData(baseItem, details, progress) {const merged = { ...baseItem };if (details) Object.assign(merged, details);this.processProjectStatus(merged);  // 提前处理状态if (progress) merged.info = progress;return merged;},// 状态处理逻辑processProjectStatus(project) {if (project.projectStatus) {const status = PROJECT_STATUS_MAP.find(item => item.value === project.projectStatus);project.statusLabel = status?.label || project.projectStatus;project.statusColor = status?.color || '';}}

3.2 第二阶段:并行最大化(方案二)

优化目标

  • 消除串行请求
  • 缓存高频查询
  • 减少DOM操作
时间复杂度 O(n * (k + m))- n = 项目数量
- k = B 接口响应时间 
- m = C 接口响应时间
(当前串行执行导致总时间线性增长)空间复杂度 O(n)- 与项目数量成线性关系,主要存储 projectList 数据
    async loadProjectData() {this.loading = true;try {// 优化点1:使用缓存基准数据const baseRes = await this.$api.projectService.AA({ clientId: this.customer.clientId });if (baseRes.code !== 0 || !baseRes.data?.length) return;// 优化点2:并行处理请求const projectPromises = baseRes.data.map(async (baseItem) => {try {// 优化点3:并行请求子接口const [detailRes, progressRes] = await Promise.all([this.$api.projectService.BB({ projectCode: baseItem.projectCode }),this.$api.projectService.CC({projectCode: baseItem.projectCode,projectStatus: baseItem.projectStatus // 假设基准数据包含状态})]);// 优化点4:使用对象解构提升合并效率return {...baseItem,...(detailRes.data?.records[0] || {}),info: progressRes.data || null,statusLabel: this.getStatusLabel(baseItem.projectStatus),statusColor: this.getStatusColor(baseItem.projectStatus)};} catch (error) {console.error('子请求失败:', error);return baseItem; // 保持基础数据}});// 优化点5:批量更新数据this.projectList = await Promise.all(projectPromises);} catch (error) {console.error('主请求失败:', error);} finally {this.loading = false;}},// 新增状态映射方法getStatusLabel(status) {return PROJECT_STATUS_MAP.find(item => item.value === status)?.label || status;},getStatusColor(status) {return PROJECT_STATUS_MAP.find(item => item.value === status)?.color || '';}
优化点原方案新方案提升幅度
接口调用方式串行执行完全并行50%+
状态查询效率O(n) 遍历查找O(1) 缓存查询300%
数据合并方式多次对象赋值单次解构赋值40%
错误处理机制全流程中断子请求容错100%
DOM 更新次数多次更新单次批量更新60%

3.3 第三阶段:稳定性加固(方案三)

 async loadProjectData() {this.loading = true;try {const baseRes = await this.$api.projectService.AA({ clientId: this.customer.clientId });if (baseRes.code !== 0 || !baseRes.data?.length) return;// 使用 allSettled 保证部分失败不影响整体const settledResults = await Promise.allSettled(baseRes.data.map(async (baseItem) => {try {// 先获取必要详情数据const detailRes = await this.$api.projectService.BB({ projectCode: baseItem.projectCode });// 从详情响应中获取最新状态const currentStatus = detailRes.data?.records[0]?.projectStatus;// 并行获取其他数据const [progressRes] = await Promise.all([this.$api.projectService.CC({projectCode: baseItem.projectCode,// 使用最新状态,降级逻辑保障基础功能projectStatus: currentStatus || baseItem.projectStatus })]);return {...baseItem,...(detailRes.data?.records[0] || {}),info: progressRes.data || null,statusLabel: this.getStatusLabel(currentStatus),statusColor: this.getStatusColor(currentStatus)};} catch (error) {console.error('子请求失败:', error);return { ...baseItem, _error: true }; // 标记错误项}}));// 过滤处理成功的数据this.projectList = settledResults.filter(result => result.status === 'fulfilled' && !result.value._error).map(result => result.value);} catch (error) {console.error('主请求失败:', error);} finally {this.loading = false;}},// 新增状态映射方法getStatusLabel(status) {return PROJECT_STATUS_MAP.find(item => item.value === status)?.label || status;},getStatusColor(status) {return PROJECT_STATUS_MAP.find(item => item.value === status)?.color || '';}

主要优化点:

  1. 状态获取优化:从 B 接口响应获取最新状态,仅当状态不存在时使用基准数据
  2. 分级错误处理:
  • 使用 Promise.allSettled 保证单个项目失败不影响整体
  • 添加错误标记 _error 过滤无效数据
  • 保留基准数据用于降级展示
  1. 请求顺序调整:
  • 先获取包含状态的详情数据
  • 基于最新状态请求进展数据
  • 保持项目级别的并行处理

优化后特性:

  • 状态数据时效性:✅ 使用最新接口返回的状态
  • 错误容忍度:✅ 单项目错误不影响整体列表
  • 降级展示:✅ 即使子接口全失败仍显示基础信息
原始方案最终方案提升幅度
总耗时12.8s2.1s83%
内存占用34MB28MB18%
错误恢复率0%92%-

4. 未来优化

  1. 请求合并:与后端协商批量查询接口
  2. 缓存策略:添加本地缓存过期机制
  3. 性能监控:接入APM系统进行实时监控
  4. Skeleton优化:增加数据加载占位动画

“文中代码已进行脱敏处理,关键路径和参数均为示例数据”

相关文章:

基于Promise链式调用的多层级请求性能优化

代码优化-循环嵌套关联请求 1. 背景 在实际开发中&#xff0c;我们经常会遇到需要嵌套关联请求的场景&#xff0c;比如&#xff1a; 获取项目列表获取项目详情获取项目进度 2. 问题 在这种场景下&#xff0c;我们可能会遇到以下问题&#xff1a; 串行请求瀑布流&#xff…...

【强化学习】基于深度强化学习的微能源网能量管理与优化策略研究【Python】

目录 主要内容 程序要点 2.1 微能源网系统组成 2.2 强化学习及Q学习算法 部分代码 运行结果 下载链接 主要内容 该程序借助深度 Q 网络&#xff08;DQN&#xff09;&#xff0c;学习预测负荷、风 / 光可再生能源功率输出及分时电价等环境信息&#xff0c;运用…...

楼宇自控借何种技术,驱动建筑迈向高效绿色

在全球积极倡导可持续发展的大背景下&#xff0c;建筑行业作为能源消耗和碳排放的大户&#xff0c;实现高效绿色发展迫在眉睫。楼宇自控系统凭借其先进的技术手段&#xff0c;成为推动建筑向高效绿色转型的关键力量。那么&#xff0c;楼宇自控究竟借助哪些技术&#xff0c;让建…...

监控易一体化运维:监控易机房管理,打造高效智能机房

在数字化浪潮中&#xff0c;企业对数据中心和机房的依赖程度与日俱增&#xff0c;机房的稳定运行成为业务持续开展的关键支撑。信息化的变迁&#xff0c;见证了机房管理从传统模式向智能化、精细化转变的过程。今天&#xff0c;就为大家深度剖析监控易在机房管理方面的卓越表现…...

简记_FPGA 硬件最小系统设计

一、FPGA板级设计的五要素 1.1、电源电路 核心电压&#xff1a;一般为固定值 IO电压&#xff1a;FPGA的IO分为多个bank&#xff0c;同一个bank的不同IO引脚电压相同&#xff0c;不同bank的电压可以不同 辅助电压&#xff1a;除了核心电压和IO电压&#xff0c;FPGA工作所需的…...

1.1-站点差异\源码差异\数据存储差异\MVC模型

1、有哪几种站点 分主站、分站、端口站、子站、目录站 2、有哪几种源码语言框架差异 开源-如Zblog 闭源-内部开发 加密-如通达OA 3、网站数据存储有哪几个方式 本地数据库&#xff1a;本地服务器搭建 分离数据库&#xff1a;另外的服务器搭建 云数据库&#xff1a;RDS…...

PHP安装HTML转图片的扩展GD库的使用

修改你的PHP.ini文件,找到以下位置 ;extensionphp_gd2.dll 把前面的;去掉…...

清华大学第10讲:迈向未来的AI教学实验396页PPT 探索未来教育的无限可能|附PPT下载方法

导 读INTRODUCTION 今天跟大家分享的是清华大学新闻与传播学院、人工智能学院双聘教授沈阳教授团队出品的《迈向未来的AI教学实验》课程作业集&#xff0c;随着人工智能技术的飞速发展&#xff0c;教育领域也迎来了前所未有的变革。该报告为沈阳教授与学生们在“迈向未来的AI教…...

《白帽子讲 Web 安全》之服务端请求伪造(SSRF)深度剖析:从攻击到防御

引言 在当今复杂的网络环境中&#xff0c;Web 应用安全犹如一座时刻需要精心守护的堡垒。随着技术的不断演进&#xff0c;各类安全威胁层出不穷&#xff0c;其中服务端请求伪造&#xff08;SSRF&#xff09;正逐渐成为令开发者与安全从业者头疼的一大难题。吴翰清在《白帽子讲…...

豪越消防一体化安全管控平台:消防管理智能化

在社会快速发展、城市建设日益复杂的今天&#xff0c;消防安全始终是保障人民生命财产安全、维护社会稳定的重要基石。传统消防管理模式在应对当下复杂多变的消防安全需求时&#xff0c;逐渐暴露出诸多局限性&#xff0c;而豪越消防一体化平台的出现&#xff0c;为消防管理领域…...

VMware虚拟机 ubuntu22.04无法与共享粘贴板和拖拽文件的解决方案

VMware虚拟机 ubuntu22.04无法与共享粘贴板和拖拉文件的解决方案 卸载VMware tools安装open-vm-tools还无法拖拽文件 卸载VMware tools 确保卸载完vmware-tools # 进入vmware-tools安装目录/bin sudo vmware-uninstall-tools.pl sudo rm -rf /usr/lib/vmware-tools sudo apt-…...

瑞芯微RK356X主板复用接口配置方法,触觉智能嵌入式方案商

本文介绍瑞芯微RK356X系列复用接口配置的方法&#xff0c;基于触觉智能RK3562开发板演示&#xff0c;搭载4核A53处理器&#xff0c;主频高达2.0GHz&#xff1b;内置独立1Tops算力NPU&#xff0c;可应用于物联网网关、平板电脑、智能家居、教育电子、工业显示与控制等行业。 复…...

企业签名app部分用户能安装,部分用户不能安装

企业签名的app有的用户能安装&#xff0c;有的用户安装失败。 最近在做企业签名后&#xff0c;有客户反馈&#xff0c;客户的手机能安装&#xff0c;但是别人的手机安装不了。 一般有3种情况&#xff1a; 1、首先确保配置文件info.plist里面都添加了SSL证书(HTTPS)。 2、开发人…...

NX二次开发刻字功能——预览功能

这个预览功能其实在NX软件中很常见,有利于建模者确定刻字的位置,这个功能早在唐康林老师的超级长方体教程中出现过。我只是学以致用。把该功能集成刻字中。 在勾选预览的同时,如果点击放大镜也就是显示预览结果,要刻字的对象透明度数值为70,同时预览结果文字会变成撤销,如…...

前端知识点---window.location.assign() 和 window.location.href 的区别(javascript)

window.location.assign() 和 window.location.href 的主要区别&#xff1a; 读取和设置 window.location.href&#xff1a;既可以读取当前 URL&#xff0c;也可以通过赋值更改 URL。 window.location.assign()&#xff1a;只能用于跳转到新的 URL&#xff0c;不能读取当前地…...

容器主机CPU使用率突增问题一则

关键词 LINUX、文件系统crontab 、mlocate根目录使用率 There are many things that can not be broken&#xff01; 如果觉得本文对你有帮助&#xff0c;欢迎点赞、收藏、评论&#xff01; 一、问题现象 业务一台容器服务器&#xff0c;近期经常收到cpu不定期抖动告警&#x…...

华为hcia——Datacom实验指南——配置IPv4静态路由,默认路由和浮动静态路由

什么是IPv4 IPv4静态路由&#xff0c;是手动配置的&#xff0c;不会随着网络拓扑的变化而变化&#xff0c;所配置的路由信息也不会在网络中传播&#xff0c;所以它主要运用在小型网络或者作为动态路由的补充。 IPv4的配置 配置的命令很简单 IP route-static &#xff08;目…...

解释时间复杂度 O() 表示法,如何评估算法效率?

时间复杂度与前端开发实战指南 作为前端工程师&#xff0c;理解时间复杂度能帮助我们写出高性能代码。以下是结合前端场景的深度解析&#xff1a; 一、时间复杂度的本质 时间复杂度用大O符号表示算法执行时间随数据规模增长的变化趋势。​关注的是最坏情况下增长的量级&…...

【Apache Hive】

一、Hive简介 官网&#xff1a;https://hive.apache.org 1、Hive是什么&#xff1f; Apache Hive 是一款建立在Hadoop之上的开源数据仓库系统&#xff0c;可以将存储在Hadoop文件中的结构化、半结构化数据文件映射为一张数据库表&#xff0c;基于表提供了一种类似SQL的查询模型…...

SQL Server安装进度卡在 57%:Windows Update 服务异常

问题现象&#xff1a; 安装 SQL Server 2022 时进度停滞在 57%&#xff0c;日志报错 Error code 0x80070422&#xff0c;提示 “Windows Update 服务未运行”。 快速诊断 检查服务状态&#xff1a; # 查看 Windows Update 服务状态 Get-Service -Name wuauserv | Select-Object…...

YOLO历代发展 图像增强方式 架构

YOLO1 YOLOV5 数据增强 mosaic 仿射变换(Affine)、透视变换(Perspective) 网络搭建...

DexGrasp Anything:具有物理-觉察的普遍机器人灵巧抓取

25年3月来自上海科技大学的论文“DexGrasp Anything: Towards Universal Robotic Dexterous Grasping with Physics Awareness”。 能够抓取任何物体的灵巧手&#xff0c;对于通用具身智能机器人的开发至关重要。然而&#xff0c;由于灵巧手的自由度高&#xff0c;物体种类繁多…...

对称加密算法和非对称加密算法

在这个互联网普及的时代&#xff0c;在不同终端对敏感甚至机密数据进行传输是非常常见的场景&#xff0c;但是如何保证数据传输过程的安全性和高效性是一个值得深入探讨的问题。 为此&#xff0c;伟大的人类研究出了多种加密算法&#xff0c;我们可以大致将其分为两类&#xf…...

行为型——责任链模式

责任链模式 责任链模式是一种行为型设计模式&#xff0c;它允许请求沿着处理链传递&#xff0c;直到有一个处理者处理为止。其核心思想是解耦请求发送者与接收者。 特点 解耦处理者与接收者: 发送者无需知道哪一个接收者处理请求&#xff0c;接收者也无需知道请求的细节。动…...

<wbr>标签的用途,在处理长文本换行时如何发挥作用?

大白话标签的用途&#xff0c;在处理长文本换行时如何发挥作用&#xff1f; 在 HTML 里&#xff0c;<wbr> 标签可以说是处理长文本换行问题的一把利器。接下来&#xff0c;我会详细说明它的用途&#xff0c;以及在处理长文本换行时发挥的作用 <wbr> 标签用途概述…...

【C#】关键字 volatile

volatile 是 C# 中一个用于多线程编程的关键字&#xff0c;作用是确保变量的可见性 —— 也就是说&#xff0c;一个线程对变量的修改&#xff0c;其他线程可以马上“看到”。 &#x1f50d; 为什么需要 volatile&#xff1f; 在多线程中&#xff0c;CPU 和编译器可能会对代码进…...

【计算机网络】OSI七层模型完全指南:从比特流到应用交互的逐层拆解

OSI模型 导读一、概念二、模型层次结构2.1 物理层&#xff08;Physical Layer&#xff09;2.2 数据链路层&#xff08;Data Link Layer&#xff09;​2.3 ​网络层&#xff08;Network Layer&#xff09;​2.4 ​传输层&#xff08;Transport Layer&#xff09;​2.5 ​会话层&…...

数据不互通、审批慢?如何实现多系统智能协同

在企业信息化建设的过程中&#xff0c;数据孤岛和复杂的审批流程常常成为实现高效协同的巨大障碍。对于许多组织来说&#xff0c;面对越来越复杂的业务需求&#xff0c;如何实现多系统智能协同不仅关乎效率&#xff0c;更直接影响企业的竞争力。 数据不互通和审批流程慢的痛点…...

量子力学:从经典物理危机到思维革命的新纪元

目录 一、经典物理的黄昏&#xff1a;危机四伏的 “完美大厦” &#xff08;一&#xff09;牛顿力学的辉煌与局限 &#xff08;二&#xff09;麦克斯韦电磁理论的成就与困境 &#xff08;三&#xff09;热力学与统计物理学的发展与难题 二、旧量子论的破晓&#xff1a;突破…...

深入解析 JSON-RPC:从基础到高级应用(附调用示例)

在当今的软件开发领域&#xff0c;远程过程调用&#xff08;RPC&#xff09;技术是实现分布式系统间通信的关键手段之一。JSON-RPC&#xff0c;作为一种基于 JSON 数据格式的轻量级 RPC 协议&#xff0c;因其简洁性和高效性而备受青睐。本文将全面深入地探讨 JSON-RPC 的核心概…...