异步递归中try-catch必须每层独立包裹,确保每次await和递归调用都在try内;重试需await+延迟+次数判断,最后抛出明确错误;finally仅做同步清理,避免return/throw;错误应按类型(如401、404、网络超时)差异化处理。

在异步递归函数中,try-catch 不是包一层就万事大吉的事。它必须和每次递归调用的异步执行上下文严格对齐,否则错误会漏捕、重试中断,甚至触发无限循环或 unhandledrejection。
每层递归都得单独包裹 try-catch
异步递归的本质是:当前层 await 完成后,再发起下一次调用。如果只在外层 try 里调用第一次,后续递归就脱离保护范围——错误直接冒泡到全局,catch 根本收不到。
- ✅ 正确做法:把整个递归逻辑(包括 await 和下一次调用)写在 try 块内,让每次调用都在自己的 try-catch 作用域里
- ❌ 错误写法:外层 try 调用一次 async 函数,然后在它的 catch 里直接写函数名不 await,比如
fetchWithRetry(url, retries - 1)—— 这返回的是 Promise,错误仍可能未处理 - 关键点:递归调用前必须确保它被 await,且 await 表达式本身在 try 内部
重试逻辑要嵌在 catch 里,且必须 await
重试不是“再调一次”那么简单,它需要明确控制节奏,并保证异常不逃逸。
- 重试前建议加延迟(如
await new Promise(r => setTimeout(r, 1000))),避免密集失败打垮服务 - 重试次数递减后,要判断是否还有余量;用
if (retries > 0)控制递归入口,避免无意义调用 - 最后一次失败时,应抛出有意义的错误(如
throw new RetryExhaustedError(...)),而不是静默返回或忽略
finally 用于清理,别让它干扰流程
异步递归常伴随临时资源(定时器、AbortController、锁等),这些必须在每层结束时释放,不管成功还是失败。
- 在 try-catch 后加 finally,统一做清理动作,比如
controller?.abort()或clearTimeout(timer) - 避免在 finally 里 return 或 throw —— 它会覆盖 try/catch 的返回值或错误,导致逻辑错乱
- 清理动作本身也应尽量同步、轻量,防止阻塞主线程或引入新异步风险
错误类型要区分处理,别一锅炖
不是所有异常都该重试。网络中断、认证失效、资源不存在,应对策略完全不同。
- 对
TypeError(如 fetch 失败)或连接超时,可重试 - 遇到 401 状态,应刷新 token 或跳转登录,而不是反复请求
- 404 或业务校验失败(如参数非法),通常不该重试,而应快速失败并反馈用户
- 建议用
err.name、err.status或响应体字段做细粒度判断,而不是只看 message 字符串
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











