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

接口正常被调用且返回数据但前端页面渲染失败,控制台报错Uncaught (in promise)

问题描述

接口正常被调用且返回数据但前端页面渲染失败,控制台报错 Uncaught (in promise) error

在这里插入图片描述

Uncaught (in promise) error @ asyncToGenerator.js:6
Promise.then		
getDeivceList	@	index.vue:103
mounted	@	index.vue:124
Promise.then		
(匿名)	@	permission.js:130
Promise.then		
(匿名)	@	permission.js:130
(匿名)	@	permission.js:41
(匿名)	@	permission.js:32
Promise.then		
(匿名)	@	permission.js:29
Promise.then

这些Promise错误就像调皮的小鬼,在调用栈里上蹿下跳。最气人的是,在我本地环境竟然能复现!

解决思路(TL;DR:)

经过排查,发现:

  1. 接口请求虽然返回了数据,但返回的code值为0,而非预期的200。导致项目中响应拦截器抛出了未捕获的Promise错误。

导致产生这种情况的原因是:

  1. 后端同学未按照规范返回指定的响应码
  2. 前端同学在编写响应拦截器时未考虑更多的边界条件,未有错误预警。

破案之旅

先来一套"前端调试三板斧":

  1. 打断点追踪:从组件渲染层一路追踪到数据请求
  2. 查看网络响应:response里数据确实存在
  3. 数据类型校验:确定不是undefined在作妖
    当检查到响应拦截器时,突然眼前一亮
// 响应拦截器
service.interceptors.response.use(
  (res) => {
    // 未设置状态码则默认成功状态
    const code = res.data.code || 200;
    // 获取错误信息
    const msg = errorCode[code] || res.data.msg || errorCode["default"];
    // 二进制数据则直接返回
    if (
      res.request.responseType === "blob" ||
      res.request.responseType === "arraybuffer"
    ) {
      return res.data;
    }
    if (code === 401) {
      if (!isRelogin.show) {
        isRelogin.show = true;
        MessageBox.confirm(
          "登录状态已过期,您可以继续留在该页面,或者重新登录",
          "系统提示",
          {
            confirmButtonText: "重新登录",
            cancelButtonText: "取消",
            type: "warning",
          }
        )
          .then(() => {
            isRelogin.show = false;
            store.dispatch("LogOut").then(() => {
              location.href = "/index";
            });
          })
          .catch(() => {
            isRelogin.show = false;
          });
      }
      return Promise.reject("无效的会话,或者会话已过期,请重新登录。");
    } else if (code === 500) {
      // Message({ message: msg, type: "error" });
      return Promise.reject(new Error(msg));
    } else if (code === 601) {
      Message({ message: msg, type: "warning" });
      return Promise.reject("error");
    } else if (code !== 200) {
      Notification.error({ title: msg });
      return Promise.reject("error");
    } else {
      return res.data;
    }

这里有个很魔性的判断:code !== 200就会进错误处理。但你猜怎么着?后端同学在需求评审时说好的用200表示成功,结果上线时他们实际返回的code字段竟然是0!这就好比说好握手用右手,结果对方出左手。

于是当前端判断到code不是200时,直接触发reject:

// 原始代码判断逻辑
if (code !== 200) {
    Notification.error({ title: msg });
    return Promise.reject("error"); // 这里才是罪魁祸首
}

这导致调用链里的Promise异常没有被正确捕获,最终导致整个渲染流程崩溃。

后记

现在每次写拦截器都会留个后手:

我们给所有关键接口加了埋点监控,类似这样:

// 异常数据上报逻辑
if (unexpectedCode) {
    track({
        event: 'CODE_MISMATCH',
        payload: {
            expected: 200,
            actual: code,
            path: location.href
        }
    });
}

后记

现在每次对接新接口,我都会先确认这三个致命点:

  1. 状态码字段名叫code还是status?
  2. 正确的成功状态是数字类型还是字符串?
  3. 异常时的数据结构是否统一?

FAQ(来自团队内部提问)
Q:为什么不直接让后端改回200?
A:当时涉及多个正在测试中的下游系统,改动成本较高,故采用临时方案过渡
Q:Promise错误为何会导致整个页面崩溃?
A:由于我们在顶层没有写window.addEventListener(‘unhandledrejection’)事件监听
Q:怎么避免类似问题再次发生?
A:现在PR提交时会强制要求接口字段快照对比

👨💻 后来跟后端兄弟撸串时聊起这事,他说自己当时看错了文档里的状态码定义… 果然是血与泪的教训啊!


http://www.kler.cn/a/584964.html

相关文章:

  • react对比vue的核心属性
  • 【水文模型】地理信息系统(ArcGIS)在水文水资源、水环境中的应用
  • VBA即用型代码手册:选择、转到Select、 Go To
  • 【Python】Linux 升级 Python 版本(源码安装)
  • 地理信息系统(ArcGIS)在水文水资源及水环境中的应用:空间数据管理‌、空间分析功能‌、‌可视化表达‌
  • 【后端】【django drf】Django DRF API 编写规范(程序设计规则)
  • # RAG 框架 # 一文入门 全链路RAG系统构建与优化 —— 架构、策略与实践
  • 2025 香港 Web3 嘉年华:全球 Web3 生态的年度盛会
  • 界面控件DevExpress Blazor UI v24.2新版亮点:支持.NET 9
  • 批量在多个在 Excel 工作表的的指定位置插入新的 Sheet 工作表
  • USB数据采集卡 Labview采集卡 32路AD模拟量采集 DAQ卡
  • Ceph(1):分布式存储技术简介
  • shell变量
  • Spring Boot + Vue 基于RSA+AES的混合加密
  • 训练数据重复采样,让正负样本比例1:1
  • 【开源项目-爬虫】Firecrawl
  • MySQL行列转化
  • 开VR大空间体验馆,如何最低成本获取最大收入?
  • 深度学习环境配置指令大全
  • go-文件缓存与锁