finally 是资源清理的必经之地,无论 await 成功、拒绝或抛错都会执行;异步清理需用 IIFE 封装并捕获错误;定时器、监听器、WebSocket 等须在 finally 中显式关闭;避免 finally 内抛错或返回值。

async/await 本身不改变 try...catch...finally 的执行逻辑,但资源清理必须放在 finally 块中,且要确保清理操作本身是同步的,或对异步清理做显式等待(需注意函数是否仍为 async)。
finally 是清理的“必经之地”
无论 await 的 Promise 成功、拒绝,还是中间 throw 了错误,finally 块都会执行。这是保证资源释放最可靠的位置。
- 定时器、事件监听器、WebSocket 连接、Canvas 上下文等,都应在 finally 中主动关闭或移除
- 避免在 try 或 catch 中 return 或 throw 后遗漏清理——finally 能兜底
- 示例中 fetch 失败、JSON 解析出错、甚至手动 throw,finally 里的 close() 都会运行
异步清理操作不能直接写在 finally 里
finally 块内代码是同步执行的。如果清理本身需要 await(比如调用一个返回 Promise 的 destroy() 方法),就不能直接 await,否则会静默失败或抛出未处理的 Promise rejection。
- 正确做法:把异步清理封装进 IIFE 并 .catch() 忽略错误,或在外部 async 函数中提前 await 清理
- 推荐结构:整个函数保持 async,将 await 清理逻辑放在 finally 内部的立即执行函数中,并捕获其错误
- 例如:void (async () => { await resource.destroy(); })().catch(() => {});
常见资源类型与清理方式
不同资源需对应不同清理策略,关键是要有明确的“释放入口”:
- 定时器:保存 setTimeout/setInterval 返回的 ID,在 finally 中 clearTimeout/clearInterval
- 事件监听器:使用具名函数或绑定后的引用,在 finally 中 removeEventListener
- WebSocket / EventSource:检查 readyState,调用 .close(),并置空 onmessage 等回调引用
- 第三方库实例(如 Chart.js、Mapbox):查阅文档,调用 .destroy()、.remove() 或 .off()
- 手动分配的内存引用(如大数组、缓存对象):设为 null 或清空属性,辅助 GC
别让 finally 成为错误黑洞
finally 里执行出错不会中断流程,但可能掩盖真实问题或导致二次异常。
- 避免在 finally 中做复杂逻辑或网络请求;专注“释放”,不做“业务”
- 若清理操作可能失败(如 close() 报错),应包裹 try...catch,至少 console.warn 记录
- 不要在 finally 中 return 或 throw —— 会覆盖 try/catch 中的返回值或错误











