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

Vue3并发请求Promise.allSettled的结果处理优化示例

原来的代码:

// 发送网络请求获取数据 const fetchData = async (id: number, workflowInstanceId: number, activityId: number) => { const currentId = ++requestId; loading.value = true; try { // 并发请求 const [detailResult, fieldResult, auditResult] = await Promise.allSettled([ // 获取资金分配明细列表 capitalAllocateApi.getAllocateDetail(id), // 获取活动可编辑字段 getActivityEditableFieldList(activityId), // 获取审批信息列表 getAuditInfoList(workflowInstanceId) ]); // 前置检查:组件已卸载 或 不是当前最新请求则返回 if (!isMounted || currentId !== requestId) return; // 更新数据 if (detailResult.status === "fulfilled" && detailResult.value?.data) { allocateDatas.value = detailResult.value.data; } // 等待 watch 回调执行(此时 dataLoaded 仍为 false,不会错误修改 isModified) await nextTick(); dataLoaded.value = true; // 标记数据加载完成 isModified.value = false; // 重置修改状态(未修改) if (fieldResult.status === "fulfilled" && fieldResult.value?.data) { editableFieldDatas.value = fieldResult.value.data; } if (auditResult.status === "fulfilled" && auditResult.value?.data) { auditDatas.value = auditResult.value.data; } // 处理错误并友好提示 const hasDetailError = detailResult.status !== "fulfilled" || !detailResult.value?.data; const hasFieldError = fieldResult.status !== "fulfilled" || !fieldResult.value?.data; const hasAuditError = auditResult.status !== "fulfilled" || !auditResult.value?.data; if (hasDetailError && hasAuditError) { ElMessage.error("数据加载失败,请稍后重试"); } else { if (hasDetailError) ElMessage.error("资金分配明细加载失败"); if (hasFieldError) ElMessage.error("可编辑字段加载失败"); if (hasAuditError) ElMessage.error("审核信息加载失败"); } // 渐进式加载(核心数据先展示,次要数据后加载),获取办理选项 const activityInstance = props.capitalAllocate?.activityInstance; if (activityInstance) { // 前置检查:组件已卸载 或 不是当前最新请求则返回 if (!isMounted || currentId !== requestId) return; getAuditOption(activityInstance) .then((result) => { // 前置检查:组件已卸载 或 不是当前最新请求则返回 if (!isMounted || currentId !== requestId) return; auditOption.value = result.data; }) .catch((error) => { // 前置检查:组件已卸载 或 不是当前最新请求则返回 if (!isMounted || currentId !== requestId) return; console.error("获取办理选项失败:", error); }); } } catch (error) { console.error("请求过程发生意外错误", error); ElMessage.error("系统错误,请稍后重试"); } finally { if (isMounted && currentId === requestId) loading.value = false; } };
错误处理代码冗余:fetchData 中逐个判断并提示,可读性较差。 代码臃肿,维护成本高。 可封装一个统一处理函数,或使用 try-catch 结合错误状态码区分。 这里应该如何修改

针对fetchData中错误处理代码冗余的问题,可以通过封装统一处理函数结构化请求配置来优化。以下是两种可行的改进方案,重点保持原有业务逻辑(核心数据明细+审批都失败时显示整体失败,其他情况分别提示)。


方案一:封装通用处理函数

将请求结果的处理逻辑抽象为一个函数,接收请求结果、对应的数据 ref、错误消息和是否为“核心数据”标识。通过遍历请求配置数组,统一处理。

代码示例

typescript

const fetchData = async (id: number, workflowInstanceId: number, activityId: number) => { const currentId = ++requestId; loading.value = true; try { // 定义请求配置列表 const requests = [ { promise: capitalAllocateApi.getAllocateDetail(id), ref: allocateDatas, errorMsg: '资金分配明细加载失败', isCore: true, // 核心数据 }, { promise: getActivityEditableFieldList(activityId), ref: editableFieldDatas, errorMsg: '可编辑字段加载失败', isCore: false, }, { promise: getAuditInfoList(workflowInstanceId), ref: auditDatas, errorMsg: '审核信息加载失败', isCore: true, }, ]; // 并发执行所有请求 const results = await Promise.allSettled(requests.map(r => r.promise)); // 前置检查:组件已卸载 或 不是当前最新请求则返回 if (!isMounted || currentId !== requestId) return; // 统计核心数据成功/失败数量 let coreSuccessCount = 0; let coreFailCount = 0; const errors: string[] = []; // 遍历结果,依次处理 results.forEach((result, index) => { const config = requests[index]; if (result.status === 'fulfilled' && result.value?.data) { // 成功且有数据 config.ref.value = result.value.data; if (config.isCore) coreSuccessCount++; } else { // 失败或无数据 if (config.isCore) coreFailCount++; errors.push(config.errorMsg); } }); // 等待可能的 watch 回调执行 await nextTick(); dataLoaded.value = true; isModified.value = false; // 核心数据全部失败,提示整体错误;否则分别提示 if (coreSuccessCount === 0 && coreFailCount > 0) { ElMessage.error('数据加载失败,请稍后重试'); } else { errors.forEach(msg => ElMessage.error(msg)); } // 获取办理选项(渐进加载) const activityInstance = props.capitalAllocate?.activityInstance; if (activityInstance) { if (!isMounted || currentId !== requestId) return; getAuditOption(activityInstance) .then((result) => { if (!isMounted || currentId !== requestId) return; auditOption.value = result.data; }) .catch((error) => { if (!isMounted || currentId !== requestId) return; console.error('获取办理选项失败:', error); }); } } catch (error) { console.error('请求过程发生意外错误', error); ElMessage.error('系统错误,请稍后重试'); } finally { if (isMounted && currentId === requestId) loading.value = false; } };

优点

  • 逻辑集中:错误处理与请求配置解耦,维护时只需修改配置数组。

  • 可扩展性强:新增请求只需在requests中添加一项。

  • 清晰统计核心数据状态:统一判断是否需要显示“整体失败”。


方案二:使用辅助函数处理单个请求结果

定义一个小函数handleRequestResult,专门处理单个 Promise 的结果,并返回是否成功。然后在主函数中调用。

代码示例

typescript

const fetchData = async (id: number, workflowInstanceId: number, activityId: number) => { const currentId = ++requestId; loading.value = true; try { // 并发请求 const [detailResult, fieldResult, auditResult] = await Promise.allSettled([ capitalAllocateApi.getAllocateDetail(id), getActivityEditableFieldList(activityId), getAuditInfoList(workflowInstanceId) ]); if (!isMounted || currentId !== requestId) return; // 辅助函数:处理单个结果,返回是否成功(fulfilled且有数据) const handleResult = (result: PromiseSettledResult<any>, ref: any, errorMsg: string): boolean => { if (result.status === 'fulfilled' && result.value?.data) { ref.value = result.value.data; return true; } else { return false; } }; // 处理核心数据 const detailOk = handleResult(detailResult, allocateDatas, '资金分配明细加载失败'); const auditOk = handleResult(auditResult, auditDatas, '审核信息加载失败'); // 处理非核心数据 handleResult(fieldResult, editableFieldDatas, '可编辑字段加载失败'); await nextTick(); dataLoaded.value = true; isModified.value = false; // 核心数据判断 if (!detailOk && !auditOk) { ElMessage.error('数据加载失败,请稍后重试'); } else { if (!detailOk) ElMessage.error('资金分配明细加载失败'); if (!auditOk) ElMessage.error('审核信息加载失败'); } // 获取办理选项(同上) // ... } catch (error) { // ... } finally { // ... } };

优点

  • 代码简洁:减少了重复的if判断,错误消息直接作为参数传递。

  • 保留原有结构:仍使用Promise.allSettled和独立变量,适合少量请求。


推荐方案

如果后续可能增加更多请求,推荐方案一,因为它将请求配置化,更易于维护和扩展。如果请求数量固定且较少,方案二更简单直接。两种方案都消除了冗余代码,提高了可读性。

其他注意点

  • 无论哪种方案,都需要保留nextTick和竞态防护逻辑。

  • 渐进式加载的getAuditOption请求独立处理,因为它依赖前面的结果且非核心,保持原样即可。

按照以上方式重构后,fetchData将更加清晰,错误处理逻辑集中且易于修改。

优化后的代码:

// 发送网络请求获取数据 const fetchData = async (id: number, workflowInstanceId: number, activityId: number) => { const currentId = ++requestId; loading.value = true; try { // 并发请求 const [detailResult, fieldResult, auditResult] = await Promise.allSettled([ // 获取资金分配明细列表 capitalAllocateApi.getAllocateDetail(id), // 获取活动可编辑字段 getActivityEditableFieldList(activityId), // 获取审批信息列表 getAuditInfoList(workflowInstanceId) ]); // 前置检查:组件已卸载 或 不是当前最新请求则返回 if (!isMounted || currentId !== requestId) return; // 辅助函数:处理单个结果,返回是否成功(fulfilled且有数据) const handleResult = (result: PromiseSettledResult<any>, ref: any, errorMsg: string): boolean => { if (result.status === "fulfilled" && result.value?.data) { ref.value = result.value.data; return true; } else { return false; } }; // 处理核心数据 const detailOk = handleResult(detailResult, allocateDatas, "资金分配明细加载失败"); const auditOk = handleResult(auditResult, auditDatas, "审核信息加载失败"); // 处理非核心数据 handleResult(fieldResult, editableFieldDatas, "可编辑字段加载失败"); // 等待 watch 回调执行(此时 dataLoaded 仍为 false,不会错误修改 isModified) await nextTick(); dataLoaded.value = true; isModified.value = false; // 核心数据判断 if (!detailOk && !auditOk) { ElMessage.error("数据加载失败,请稍后重试"); } else { if (!detailOk) ElMessage.error("资金分配明细加载失败"); if (!auditOk) ElMessage.error("审核信息加载失败"); } // 渐进式加载(核心数据先展示,次要数据后加载),获取办理选项 const activityInstance = props.capitalAllocate?.activityInstance; if (activityInstance) { // 前置检查:组件已卸载 或 不是当前最新请求则返回 if (!isMounted || currentId !== requestId) return; getAuditOption(activityInstance) .then((result) => { // 前置检查:组件已卸载 或 不是当前最新请求则返回 if (!isMounted || currentId !== requestId) return; auditOption.value = result.data; }) .catch((error) => { // 前置检查:组件已卸载 或 不是当前最新请求则返回 if (!isMounted || currentId !== requestId) return; console.error("获取办理选项失败:", error); }); } } catch (error) { console.error("请求过程发生意外错误", error); ElMessage.error("系统错误,请稍后重试"); } finally { if (isMounted && currentId === requestId) loading.value = false; } };
http://www.jsqmd.com/news/473806/

相关文章:

  • AutoGen Studio在农业领域的应用:智能作物监测
  • RMBG-1.4开源模型优化:AI净界FP16推理提速40%与显存占用实测报告
  • OpenDataLab MinerU实测分享:扫描件文字提取准确率提升技巧
  • 基于STC89C52RC的双模遥控抓取小车设计
  • FLUX.2-klein-base-9b-nvfp4在网络安全中的应用:恶意图像样本的识别与净化转换
  • 中国大学MOOC英语词汇速记法:5个高频短语拆解技巧(附测验题答案)
  • 华为OD机试真题2026双机位C卷 C++ 语言 实现【主次关联成环警告】
  • Realistic Vision V5.1提示词工程:从C语言基础到算法优化思维
  • 重构虚拟表情表达!广州虚拟动力AH系列面捕头盔全解析
  • 深入解析7系列FPGA:以7a35tftg256-1为例的关键特性与应用
  • 工业及商住电锅炉优质供应商推荐指南:SZS型水管锅炉、SZS系列水管锅炉、WNS火管锅炉、汽锅炉、温度分层蓄能罐选择指南 - 优质品牌商家
  • 避坑指南:Ubuntu搭建KMS服务器时最常见的3个错误及解决方法
  • 卡梅德生物:ANGPTL3(血管生成素样蛋白3)脂质代谢核心靶点解析
  • CHORD-X生成报告的权威性构建:自动附上参考文献与数据来源引用
  • 一键部署Bidili Generator:SDXL图片生成从入门到精通,附参数秘籍
  • C#图片处理实战:5种Sizemode模式详解与适用场景对比
  • 多路分支,switch-case语句
  • AI读脸术成本对比:自建模型 vs 开源镜像部署性价比分析
  • vastbase-基本操作
  • 4. 【Blazor全栈开发实战指南】--Blazor开发环境搭建
  • REX-UniNLU处理长文本实战:文档级语义分析
  • DDColor参数详解与调优指南:控制饱和度、自然度与细节锐度的完整手册
  • 信息安全专业毕设入门指南:从选题到可落地的实战项目设计
  • Nat. Commun.:脑启发人工智能:人脑通过分离目标与不确定性实现自适应决策
  • 拒绝 any 走天下!Vue3 + TS 核心实战:Composition API 避坑指南与用户管理组件全流程
  • Mybatis进阶(一)
  • Unity实战:如何用代码动态切换MeshRenderer的多个材质球(附完整示例)
  • ChatGLM-6B在网络安全领域的应用:智能威胁检测系统开发
  • ZBlog 爆款主题宁静致远|1.6 万 + 下载、9.89 分、6 年更新,自媒体 / 资讯站首选主题
  • 2026年主流AI搜索优化服务商对比评测:如何选择靠谱的合作伙伴?