跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
JavaScript大前端

前端错误处理:try/catch 与 Promise 的正确姿势

前端错误处理容易混用 try/catch,尤其在异步和 fetch 场景下极易漏掉异常。文章区分了同步与异步的捕获边界,解释了 fetch 对 HTTP 错误码不抛异常的特点,并给出业务异常和系统异常的分类处理方法,包括 JSON 安全解析和错误上报等实践建议。

JavaCoder发布于 2026/6/30更新于 2026/8/2226 浏览
前端错误处理:try/catch 与 Promise 的正确姿势

前端错误处理:try/catch 与 Promise 的正确姿势

前端项目里,一遇到错误就到处塞 try/catch 的情况挺多见的。但另一方面,真正该处理的地方又给忘了——比如 fetch 的异常判断、JSON 解析的安全兜底。结果线上出了问题不好排查,错误提示要么太技术,要么压根没有。

要把错误处理好,关键就是两件事:知道 try/catch 能管什么、不能管什么,然后把'业务上能预料到的失败'和'真正的系统异常'分开处理。下面结合一些常见场景聊一下。

1. try/catch 到底能抓到什么

很多人的直觉是:try 块里的代码只要报错,catch 就能接住。其实只对了一半。

同步代码没问题

try {
  const obj = JSON.parse('{ invalid json }'); // 抛出 SyntaxError
  console.log(obj);
} catch (e) {
  console.error('解析失败:', e.message); // 能抓到
}

因为 JSON.parse 的异常是同步抛出的,执行还没跳出 try,所以能被 catch 捕获。

异步里的错误,try/catch 就管不着了

try {
  setTimeout(() => {
    throw new Error('异步报错'); // 这个 catch 抓不到!
  }, 0);
} catch (e) {
  console.error(e); // 不会执行
}

setTimeout 的回调在下一个事件循环才执行,那时候 try 早就走完了,错误会直接冒泡成未捕获异常。类似的,Promise 内部的 reject、Ajax 回调里的错误也这么任性,得用别的办法对付。

2. fetch 的错误处理比你想的麻烦

fetch 是什么?

简单说,fetch 是浏览器自带的 API,用来向后端发请求、拿数据。它返回一个 Promise,所以用 async/await 配合起来很顺手。

常用姿势:

const res = await fetch('/api/user/list');
const data = await res.json(); // 解析响应体为 JSON
console.log(data);

这里有几个概念:

概念解释
fetch(url)向该地址发起请求(默认 GET)
返回值Promise,需要 await
res响应对象,包含状态码、响应体等
res.json()将响应体解析为 JS 对象
res.text()获取纯文本响应体

一个常见的坑

很多人习惯这样写:

try {
  const res = await fetch('/api/user/list');
  const data = await res.json();
  return data;
} catch (e) {
  console.error('请求失败', e);
}

但这有个隐蔽问题:fetch 在收到 4xx、5xx 这些 HTTP 错误状态码时,并不会抛出异常。只有当网络彻底不通、跨域被拦截等底层错误发生时,才会抛出异常。所以上面的代码会把 404、500 当成成功处理,直接跑进 res.json(),可能接着解析到一堆乱七八糟的响应体。

怎么处理才对

要同时兜住网络异常和 HTTP 状态异常,一般这么做:

async function fetchUserList() {
  try {
    const res = await fetch('/api/user/list');
    if (!res.ok) {
      throw new Error(`HTTP ${res.status}: ${res.statusText}`);
    }
    const data = await res.json();
    return data;
  } catch (e) {
    if (e.name === 'TypeError' && e.message.includes('fetch')) {
      console.error('网络异常,请检查网络');
    } else {
      console.error('请求失败:', e.message);
    }
    throw e; // 让调用方也知道失败了
  }
}

要点:

  • 用 res.ok 先判断状态码是不是 2xx;不是就主动抛一个错误
  • res.json() 若遇非法 JSON 也会抛异常,能被同一个 catch 接住
  • 网络类错误特征比较明显(TypeError 里包含 'fetch'),可以在 catch 里区分一下提示

3. JSON 解析这个小坑,得封个 safeParse

后端返回的东西不一定靠谱:可能直接是个字符串 "用户不存在",也可能结构错乱。前端里直接 JSON.parse,一崩就是一片。

我一般会在项目里放一个 safeParse 工具函数:

function safeParse(str, fallback = null) {
  try {
    return JSON.parse(str);
  } catch (e) {
    console.warn('JSON 解析失败:', e.message, '原始内容:', str?.slice(0, 50));
    return fallback;
  }
}

const data = safeParse(response, {});

这样解析失败时不会让整个页面挂掉,而是静默回到一个兜底值(空对象或空数组),同时控制台里打一条 warn,方便查问题。

4. 业务异常和系统异常,分开处理

如果所有错误都不加区分地用 catch 糊成一团,用户界面上可能看到'网络异常'但其实是余额不足,或者技术错误细节直接暴露。所以得把两类错误分开。

业务异常——那些可以预料到的'业务上的失败':余额不足、未登录、参数错误等等。后端通常会通过业务码(如 code: 'BALANCE_INSUFFICIENT')和人类可读的消息返回。这类错误要明确提示用户,让用户知道下一步该做什么。

系统异常——网络断了、服务器 500、JSON 解析炸了、未知的运行时错误。这些不该把细节暴露给用户,应该上报监控,用户只看到一个通用的'系统繁忙'之类的提示。

下面是一个下单接口的例子,同时处理这两类:

async function placeOrder(orderData) {
  try {
    const res = await fetch('/api/order/create', {
      method: 'POST',
      body: JSON.stringify(orderData),
    });
    const text = await res.text();
    const data = safeParse(text, null);

    if (!res.ok) {
      // 业务异常:有明确的业务码
      if (data?.code === 'BALANCE_INSUFFICIENT') {
        return { success: false, type: 'business', message: '余额不足,请先充值' };
      }
      if (data?.code === 'UNAUTHORIZED') {
        return { success: false, type: 'auth', message: '请先登录' };
      }
      // 其他 4xx/5xx 当成系统异常往上抛
      return { success: false, type: 'system', message: data?.message || '服务器异常,请稍后重试' };
    }
    return { success: true, data };
  } catch (e) {
    // 系统异常:网络错误、JSON 解析异常等
    reportError(e);
    return { success: false, type: 'system', message: '网络异常,请检查网络后重试' };
  }
}

调用方就能清晰地分开提示:

const result = await placeOrder(formData);
if (result.success) {
  // 跳转成功页
} else if (result.type === 'business' || result.type === 'auth') {
  message.warning(result.message); // 可操作的业务提示
} else {
  message.error(result.message); // 系统异常,建议稍后重试
}

5. 几个实用的处理规范

  • 该用 try/catch 的地方:JSON.parse、可能抛异常的第三方库、同步业务校验逻辑
  • 别指望 try/catch 的地方:异步回调、事件监听器里的错误。这些要用 Promise 的 .catch(),或者 async/await 搭配 try/catch 包住 await 那一行
  • 全局兜底:别忘了挂一个 window.onerror 或监听 unhandledrejection,来捕获那些漏网之鱼

下面把上面提到的 safeParse 和错误分类思路整合成一个带错误上报的产品详情接口:

async function getProductDetail(id) {
  try {
    const res = await fetch(`/api/product/${id}`);
    const text = await res.text();
    const data = safeParse(text);

    if (!res.ok) {
      if (data?.code === 'NOT_FOUND') {
        return { ok: false, reason: 'product_not_found' };
      }
      throw new Error(data?.message || `请求失败:${res.status}`);
    }
    return { ok: true, data };
  } catch (e) {
    if (e.name === 'SyntaxError') {
      reportError(e, { context: 'JSON 解析', id });
      return { ok: false, reason: 'parse_error' };
    }
    if (e.message?.includes('fetch') || e.message?.includes('Network')) {
      return { ok: false, reason: 'network_error' };
    }
    throw e; // 未知错误继续向上抛,让全局兜底处理
  }
}

最后

前端错误处理没有一招鲜,关键是把'预期的业务失败'和'真正的意外异常'拆开,然后在合适的地方用合适的工具。下面这张表可以当速查用:

错误类型处理方式注意点
Ajax 网络错误try/catch + res.ok 判断4xx/5xx 不会自动抛异常
JSON 解析错误对 JSON.parse 包 try/catch封装 safeParse 复用
业务异常根据 code 分支,返回固定结构给用户明确提示
系统异常catch 后上报 + 通用提示避免暴露内部细节
异步错误Promise .catch / async try不要指望外层同步 try 捕获

记这几点,线上排查和用户体验都能轻松不少。

目录

  1. 前端错误处理:try/catch 与 Promise 的正确姿势
  2. 1. try/catch 到底能抓到什么
  3. 同步代码没问题
  4. 异步里的错误,try/catch 就管不着了
  5. 2. fetch 的错误处理比你想的麻烦
  6. fetch 是什么?
  7. 一个常见的坑
  8. 怎么处理才对
  9. 3. JSON 解析这个小坑,得封个 safeParse
  10. 4. 业务异常和系统异常,分开处理
  11. 5. 几个实用的处理规范
  12. 最后
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • OpenClaw 自托管 AI 网关安装部署指南
  • 车端部署DeepSeek-R1蒸馏模型:做法与取舍
  • 双指针算法:三数之和
  • Conceal 加密实战:保护 Android 应用敏感数据
  • GitHub Copilot 配置中文 Commit 提交规范
  • OpenClaw 配置飞书机器人教程(本地部署)
  • 2020 年 CSP-S 提高组初赛真题解析:阅读程序第 1 题
  • C++ 算法实战:图书整理 I(反转链表)从 C 到 C++ 的过渡
  • 基于 Python 和 Pygame 的彩球碰撞动画实现
  • 淘宝超市卡 TopAPI 接入实战:Spring Boot + Lombok 实现方案
  • Spring Boot 集成 MyBatis 操作数据库实战
  • FPGA 入门指南:从点亮第一颗 LED 开始
  • 哈希表数据结构原理与经典算法题解析
  • 宇树 G1 机器人强化学习训练环境搭建与奖励函数解析
  • C++ STL 关联式容器详解:map 与 set 使用
  • Web 架构深度解析:前后端分离与传统模式对比
  • Spring Bean 作用域、生命周期与自动装配源码解析
  • 黑客、骇客、红客与蓝客的概念区别及法律边界解析
  • 论文解读:K-RagRec——基于知识图谱检索增强的大语言模型推荐系统
  • Python 雷达仿真技术指南与实战案例

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online