await 永久 pending 的 Promise 本身不导致内存泄漏,但若其执行上下文持有对外部大对象的强引用,则会造成内存滞留;需避免在 async 函数中直接引用组件实例、DOM 节点等,应解构必要字段、注入超时与取消机制(如 AbortController)、清理 resolve 引用,并用 DevTools 的 Retainers 定位真实泄漏源。

async 函数中 await 一个永久 pending 的 Promise,本身不会直接造成内存泄漏;但若该 Promise 持有对外部对象(如组件实例、DOM 节点、大数组、全局缓存等)的强引用,就会让这些对象无法被垃圾回收——这才是真正的内存滞留根源。
检查 async 函数是否捕获了不该保留的大对象
await 暂停时,async 函数的执行上下文(含参数、局部变量、闭包环境)会被底层状态机持续持有,直到 Promise 决议。如果这个上下文中引用了大型数据或长生命周期对象,它们就跟着“卡住”了。
- 避免在 async 函数内直接引用整个组件实例(如 this)、未清理的 DOM 元素或巨型 Map/Set
- 把大对象提前解构或复制必要字段,而不是传入整个引用:例如用 { id, name } = item 替代直接 await 处理 item
- 对 fetch 等网络请求,慎用 await response.json() 前未判断状态——失败响应也可能返回巨量错误体,意外滞留内存
为 pending Promise 主动注入超时与取消机制
不能依赖它“自己结束”,必须人为设限。超时不是兜底,而是防止无限等待;取消则是切断引用链的关键动作。
- 网络请求优先用 AbortController:创建 signal 并传入 fetch,调用 controller.abort() 后 Promise 会以 AbortError 拒绝,上下文随之释放
- 自定义 Promise(如等待某个条件)务必配 setTimeout:在 resolve 前检查是否已超时或上下文失效,再决定是否调用
- 避免裸写 await new Promise(() => {}) —— 没有退出路径,等于手动制造内存锚点
清理外部持有的 resolve/reject 引用
很多“挂起 Promise”其实源于开发者主动保存了 resolve 函数(比如实现队列、信号等待),而忘记在时机到来时清除这些引用。
- 不要把 resolve 存在 this 上长期持有;改用 WeakMap 关联临时状态,键是当前作用域对象,值是待触发函数集合
- 在组件卸载、模块销毁、轮询停止等时机,遍历并清空所有待 resolve 的回调,或设为 null
- 若用数组管理多个 resolve,记得用 splice 或 filter 移除已失效项,而非仅置空变量
用 DevTools 定位真实泄漏源头
别只盯着 “Promise pending” 状态——它只是表象。重点看谁在 retain 它,以及它又 retain 了谁。
- 在 Chrome DevTools 的 Memory > Take Heap Snapshot 后,筛选 Promise,右键选 “Retainers” 查看哪些变量或闭包正持有它
- 关注 Closure 类型对象,特别是带 async 函数名的,展开看其闭包内是否包含 DOM、大型数组或 this 指向
- 对比快照间增长的对象,确认是否某类 Promise 实例数量持续上升且 Retainers 不变——说明引用链未断


















