高阶函数解耦异常处理:业务函数专注逻辑,异常捕获、分类、提示、重试、降级等通用能力通过可插拔策略(onSuccess/onError)动态注入,支持组合扩展与测试隔离,拦截器因缺乏业务意图无法替代。

核心思路:把“处理异常”这件事从代码里抽出来
业务函数只管做自己的事,比如查数据库、调用第三方 API、校验参数;异常怎么捕获、分类、提示、重试、降级——这些通用动作不写在业务函数内部,而是通过高阶函数动态附加。这样改日志格式、换提示组件、加监控埋点,都不用动一行业务代码。
关键设计:让高阶函数接收「策略」而非硬编码逻辑
一个真正解耦的高阶函数,签名里至少包含三类可插拔能力:
-
业务执行函数:你要跑的真实逻辑,比如
fetchUser(id) -
成功后做什么:比如更新 UI 状态、触发埋点、缓存结果,而不是固定写
setState -
失败后按规则响应:传入一个函数,接收
{ error, config, response? },内部判断是网络断开、401 登录过期、还是后端返回code !== 0,再决定跳登录页、弹 Toast、还是静默重试
不封装 alert 或 Toast.success,也不写死 if (err instanceof NetworkError) —— 这些都交给调用方传进来,高阶函数只负责调度和兜底。
典型实现:以 JavaScript/TypeScript 为例
定义一个 withErrorHandler:
function withErrorHandler<T>(
fn: () => Promise<T>,
onSuccess: (data: T) => void,
onError: (ctx: { error: unknown; config?: any; response?: Response }) => void
) {
return async () => {
try {
const data = await fn();
onSuccess(data);
return data;
} catch (error) {
onError({ error, config: fn.config }); // 可扩展上下文
throw error; // 保留原始错误,便于上层继续处理
}
};
}
使用时非常干净:
- 组件中调用:
await withErrorHandler(fetchUser, updateUI, handleBusinessError)() - 测试时可传入 mock 的
onSuccess和onError,完全不依赖 DOM 或网络 - 想加 loading 状态?再套一层
withLoading,组合使用,互不干扰
为什么拦截器或中间件不适合替代它
像 Axios 的 response.interceptors 或 Gin 的中间件,只能拿到原始响应,拿不到业务层的「意图」:
- 列表页拉取数据失败要提示用户,但定时刷新失败就该静默重试
- 上传头像失败要显示错误文案,而删除草稿失败可能连提示都不需要
- 拦截器无法区分「这个请求是否允许自动重试」「成功后要不要更新全局状态」
所以拦截器只做标准化(统一取 res.data、401 清 token),真正的业务级反馈必须留在调用点,由高阶函数桥接。

















