应统一用 error 实例承载错误信息,所有异步失败必须显式 throw new error(),禁止返回字符串、对象或自定义状态码;推荐继承 error 的自定义错误类,并附带上下文;避免“错误元组”模式,多数场景坚持 try/catch + throw;确保 promise 拒绝值和抛出值均为 error 实例;api 层成功返回结构化数据,失败仍走 throw 以保障错误传播与追踪能力。

统一用 Error 实例承载错误信息
不要返回字符串、对象字面量或自定义状态码作为“错误”,所有异步失败必须 显式 throw 一个 Error 实例。这是因为 try/catch 只捕获 throw 出来的异常,而 return { error: 'xxx' } 或 return 'failed' 会被当作正常值,彻底绕过错误处理流程。
建议做法:
- 始终使用
throw new Error('message'),避免throw 'string'或throw { code: 500 } - 可扩展为自定义错误类(如
class ValidationError extends Error),但必须继承Error,以保证堆栈、instanceof Error判断和开发者工具兼容性 - 在构造时附带必要上下文:例如
new ApiError('User not found', { status: 404, path: '/api/user/123' })
避免“错误元组”模式的隐性陷阱
像 Go 风格的 const [err, data] = await to(fetch(...)) 看似简洁,但会弱化错误语义——err 是普通变量,容易被忽略、未判断就直接解构使用,导致后续逻辑崩溃却无明确报错点。
更规范的做法是:
- 只在极少数需要“静默失败并分支处理”的场景(如兜底加载缓存)中谨慎使用
to()工具函数 - 绝大多数业务逻辑仍应依赖
try/catch+throw,让错误强制暴露、不可跳过 - 若必须用元组,确保每次解构后立即做
if (err) throw err,不把错误检查延迟到下游
Promise 链中保持错误类型一致性
在 .then() 中抛出错误、或在 async 函数中 await 后 throw,都会被自动包装为 rejected Promise。关键是要让所有环节输出的拒绝值都是 Error 实例,而非混合类型。
常见反例与修正:
- ❌
reject('network timeout')→ 应改为reject(new Error('network timeout')) - ❌
throw 'user missing'→ 应改为throw new Error('user missing') - ❌ 在
.catch()中return 'fallback'掩盖错误 → 应明确throw new FallbackError(...)或记录后重新抛出
对外暴露的 API 层做结构化封装
面向调用方(尤其是跨模块或 SDK 场景),可约定统一响应格式,但错误路径仍走 throw,成功路径才返回结构化数据。例如:
✅ 推荐接口契约:
- 成功:返回
{ data: ..., meta: { timestamp, version } } - 失败:
throw new BusinessError({ code: 'USER_NOT_FOUND', message: '用户不存在', details: { userId } })
这样既满足前端 UI 统一提示(通过 catch 捕获后提取 code 和 message),又不破坏 JavaScript 错误机制的天然传播能力,也便于日志系统按 Error.stack 追踪源头。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











