最简模板是Promise.then/catch末端统一处理,需区分网络/HTTP/业务错误并做兜底;async/await应封装为withRequestHandler高阶函数;拦截器只做标准化响应,业务提示须在调用层控制。

用 Promise.then 和 Promise.catch 封装最简成功/失败模板
直接在请求调用链末端加统一处理,是最轻量、侵入性最小的做法。适合已有大量 fetch 或 axios 调用但不想改底层的地方。
常见错误是把错误处理写成 .catch(err => { console.error(err); }) 后就结束,没做业务级兜底(比如弹提示、跳登录页、重试逻辑)。
- 成功回调里别只做
setData,先检查response.status或data.code === 0再进业务逻辑 - 失败回调必须区分网络错误(
TypeError: Failed to fetch)、HTTP 状态码错误(401、502)、业务错误(data.code !== 0),不同情况走不同分支 - 避免在
.then里抛异常来“触发”.catch—— 这会让真实网络错误和业务校验错误混在一起,难定位
用 async/await + 高阶函数抽离重复的 try/catch 模板
如果你的项目已全面用 async/await,把 try/catch 块封装成函数,比链式调用更易读、更可控。
关键不是“封装 try/catch”,而是让这个函数能接收「成功后做什么」「失败后按什么规则处理」两个策略函数。
- 函数签名建议为:
withRequestHandler(requestFn, onSuccess, onError),其中onError接收{ error, config, response? }对象,方便判断类型 - 不要在高阶函数里硬编码
alert或Toast—— 把 UI 提示能力通过参数传入,否则无法单元测试,也难以适配不同环境(如 SSR 时不能调用 DOM API) - 注意
await requestFn()抛出的可能是Response实例(fetch)或AxiosError(axios),类型不一致,需分别处理
用 axios.interceptors 全局注入成功/失败逻辑的边界
拦截器适合做跨域、token 刷新、loading 状态等通用行为,但**不适合封装业务层的成功失败模板**—— 它发生在请求发出前/响应返回后,拿不到业务代码里的 onSuccess 回调上下文。
典型误用:在 response.interceptors 里直接调用 message.success,结果所有接口都弹成功提示,包括列表页刷新、静默上传等不该打扰用户的场景。
- 响应拦截器只做「标准化响应结构」和「基础错误分类」,例如统一提取
res.data、对401自动清 token 并跳转,然后return Promise.reject(error) - 真正的「业务成功提示」必须留在组件或 service 层,由调用方决定是否需要、提示什么内容
- 如果用了
transformResponse,注意它不会处理网络异常(如断网),那些仍要靠catch或reject拦截
为什么不用 useSWR 或 react-query 的内置模板?
它们的 onSuccess/onError 是副作用钩子,不是请求流程的一部分;数据获取和状态更新是解耦的。你没法用它们替代「请求发起即执行某段逻辑」这种强控制流需求。
比如「提交表单后,无论成功失败都要关闭弹窗」—— useSWR 的 onError 只在首次请求失败时触发,重试失败不会再次调用;而你的业务逻辑需要每次失败都响应。
- 真正该复用的是「错误分类工具函数」,比如
isNetworkError、isAuthError,这些可独立测试、到处 import -
react-query的onSettled更接近你要的模板,但它不区分成功/失败值,得自己再判data !== undefined,不如手写try/catch直观 - 高阶函数的价值不在“少写几行”,而在把「什么时候提示用户」「什么时候自动重试」「什么时候上报错误」这些决策点显式暴露出来,而不是散落在各处
then里
code === 0?还是数据非空?这个判断逻辑一旦写死在高阶函数里,后面就很难针对某个接口临时绕过。留个钩子比留个默认值更重要。

















