应分层捕获异步错误:每个await单独或按语义分组包裹,网络层可重试,数据层记录日志,业务层触发ui响应;finally中避免await异步操作;并发用allsettled或子promise单独catch。

在处理复杂异步数据流(如连续请求、轮询、依赖链式调用、错误重试、并发控制)时,try-catch 本身不能直接捕获未 await 的 Promise 错误,必须配合 async/await 或 .catch() 显式介入。关键不在于“加不加”,而在于“在哪加、怎么分层、如何传递上下文”。
只包裹真正可能抛错的 await 表达式
避免把整个异步逻辑块(含校验、状态更新、UI 交互)全塞进一个 try 块里。这会导致:
- 错误定位模糊:无法判断是接口失败、解析失败,还是后续业务逻辑出错
- 副作用失控:比如 loading 状态在出错后没及时还原
- 异常被意外吞掉:非 Promise 错误(如 JSON.parse 失败)混在其中,难区分
推荐写法:每个 await 单独或按语义分组包裹,保持 try 块轻量。
示例:async function fetchUserProfile(userId) {<br> try {<br> const res = await fetch(`/api/users/${userId}`); // 只包网络请求<br> if (!res.ok) throw new Error(`HTTP ${res.status}`);<br> const data = await res.json(); // 单独处理解析<br> return validateUser(data); // 校验移出 try,失败时抛新错误<br> } catch (err) {<br> // 这里只处理 fetch 或 json 解析异常<br> throw wrapError(err, 'fetchUserProfile');<br> }<br>}
分层捕获 + 错误分类处理
复杂数据流常涉及多个环节(获取 → 转换 → 合并 → 缓存 → 渲染),不同环节失败应有不同策略:
- 网络层错误(NetworkError、5xx):可重试、降级为缓存、提示用户检查网络
- 数据层错误(JSONParseError、SchemaMismatch):记录日志、上报结构异常、返回默认空态
- 业务层错误(4xx、权限不足、库存不足):触发特定 UI 提示或跳转流程
利用 error.name、error.status、自定义字段做判断,避免只靠 message 字符串匹配。
catch (err) {<br> if (err.name === 'AbortError') return null; // 请求被取消,静默忽略<br> if (err.status >= 500) await retryWithBackoff(fetchData);<br> if (err.status === 401) redirect('/login');<br> throw err; // 其他错误继续上抛<br>}
避免在 finally 中执行异步操作
finally 块保证执行,但不能 await 异步代码——它会立即返回 Promise,无法阻塞流程或等待清理完成。常见陷阱:
- 在 finally 里调用 await logout() → 实际未等登出完成就结束函数
- 在 finally 里发埋点请求却没 await → 埋点可能丢失
正确做法:清理逻辑同步执行(如重置 loading、清空临时变量);异步清理需单独封装并显式 await,或交由上层统一管理。
与 Promise.all / race / any 配合时的错误收敛
并发请求中,Promise.all 默认“全成功才 resolve”,任一失败即 reject。若需部分容错,不要简单 try-catch 整个 all,而是:
- 用 Promise.allSettled() 获取全部结果状态,再统一分析
- 对每个子 Promise 单独 .catch(),返回 { data, error } 结构体
- 用 try-catch 包裹 allSettled 的结果遍历过程,防止后续处理出错中断主流程
这样既保留并发优势,又让错误可追溯、可聚合、可恢复。











