为何递归Promise链一个引发内存泄漏,另一个却不会?
我有两段逻辑看似一致的递归Promise循环代码,但其中一段会引发内存泄漏,另一段不会。简化后的代码如下:
泄漏代码:
function leakingLoop() { return delay(1) .then(() => { console.log(`Tick ${Date.now()}`) return leakingLoop() }) }
无泄漏代码:
function nonLeakingLoop() { delay(1) .then(() => { console.log(`Tick ${Date.now()}`) nonLeakingLoop() }) }
两者唯一的区别是leakingLoop会返回delay Promise的执行结果,而nonLeakingLoop不返回。请问为什么leakingLoop会导致内存泄漏?是不是我忽略了Promise的某些机制?
注:假设delay是一个指定时长后resolve的Promise函数,如果这个假设不符合实际,请告知我更新问题。
核心原因:Promise的链式引用与垃圾回收
这本质是因为返回Promise会构建一条无限延伸的Promise链,每个Promise都会保留对前一个Promise的引用,而不返回的版本里,每个Promise完成后没有被外部引用,能被垃圾回收器及时清理。
具体拆解:
在
leakingLoop中,每次调用leakingLoop()都会返回一个新的Promise,这个Promise是上一个delay().then(...)的返回值。Promise的then方法会创建新Promise,且新Promise会持有对原Promise(delay返回的实例)以及回调函数的引用。由于递归时把leakingLoop()的结果返回给上层then,这就形成了Promise1 -> Promise2 -> Promise3 -> ...的无限链式结构,每个后续Promise都依赖前一个节点,导致所有之前的Promise都无法被垃圾回收,内存持续累积。而
nonLeakingLoop里,delay().then(...)的结果没有被返回,意味着这个Promise没有被任何外部变量或上层Promise引用。当then回调执行完毕后,当前Promise对象就没有活跃引用了,垃圾回收器可以正常回收它占用的内存。每次递归调用nonLeakingLoop都是独立创建新的Promise,不会形成链式引用,因此不会有内存泄漏。
简单总结:返回Promise会让整个链的所有节点都被保留,不返回则每个节点用完就被回收。
内容的提问来源于stack exchange,提问作者PaOne

