应使用高阶函数封装异步错误处理,如safeRequest实现分层校验与显式抛错,配合to函数支持双返回值模式,并辅以全局错误监听兜底。

直接在每个异步函数里写 try...catch 不仅重复、易漏,还让业务逻辑被错误处理代码淹没。真正有效的做法,是把“执行 + 捕获 + 处理”这三步收口到一个高阶函数里,让调用方只关心“做什么”,不操心“出错了怎么办”。
为什么必须用高阶函数,而不是简单封装一个 catch 工具?
因为错误不是只有一种:网络中断、HTTP 状态码异常(如 401/500)、后端返回的业务错误码(data.code !== 0),它们触发时机和处理方式都不同。一个合格的封装,要能:
- 区分并保留原始错误类型(比如
TypeError和自定义ApiError) - 在 Promise resolve 后继续校验响应体,不把
200 OK + {code: 500}当作成功 - 允许调用方决定是否接管错误(比如有的接口失败就静默重试,有的则必须弹窗)
- 不侵入原有请求逻辑,比如不强制你改用 axios 或改写 fetch 调用方式
推荐封装:safeRequest —— 分层校验 + 显式抛错
这个函数接收一个「返回 Promise 的函数」(不是已执行的 Promise),确保整个链路可控:
const safeRequest = async (promiseFn, options = {}) => {
try {
const res = await promiseFn();
// ✅ 第一层:HTTP 网络层校验
if (!res.ok) throw new Error(`HTTP ${res.status}`);
<pre class="brush:php;toolbar:false;">// ✅ 第二层:业务数据层校验(按项目约定调整)
const data = await res.json();
if (data.code !== 0) {
throw new Error(data.message || `API error: ${data.code}`);
}
return data;} catch (err) { console.error('[API ERROR]', err); if (options.onError) options.onError(err); throw err; // 保持可链式处理,不吞掉错误 } };
使用时只需传入请求动作,不用再写任何 try/catch:
safeRequest(
() => fetch('/api/user'),
{ onError: (err) => notifyUser(err.message) }
)
.then(data => renderUser(data))
.catch(() => {/* 可选:兜底降级 */});
进阶用法:支持「成功/失败双返回值」模式(类似 Go)
如果你希望彻底避免 try/catch 语法,又不想用 .then().catch() 链,可以用更轻量的 to 封装:
async function to(promise) {
try {
const res = await promise;
return [res, null];
} catch (err) {
return [null, err];
}
}
<p>// 调用方式
const [userRes, userErr] = await to(fetch('/api/user').then(r => r.json()));
if (userErr) {
console.error('获取用户失败', userErr);
return;
}
renderUser(userRes);</p>
这种写法显式、无嵌套、易调试,特别适合表单提交、多步骤操作等需要逐层判断结果的场景。
配套要点:别忘了全局兜底
高阶函数管的是「主动发起的请求」,但还有三类错误它捕获不到:
-
window.addEventListener('unhandledrejection'):捕获没catch的 Promise reject -
app.config.errorHandler(Vue)或window.onerror(原生):捕获渲染、生命周期中的同步/异步异常 - 资源加载失败(图片、脚本 404):监听
window.addEventListener('error')并过滤event.target
这些和高阶函数不是替代关系,而是分层互补——前者保业务流,后者保应用稳。










