问题描述
如果在js中的async函数中await一个永远不会被解决的Promise对象,这个Promise对象在除了这个函数中不被其他任何地方引用,Promise的resolve方法也不被任何地方引用,执行这个async函数是否会导致内存泄漏?
例如考虑这段代码
async function test(p)
{
await p;
}
for(let i=0;i<10000;i++)
test(new Promise(()=>{}));
实际在Chrome观测中发现 这并不会导致内存泄漏
这符合期望的表现 因为事实上test不可能继续执行
但这是否是收到js标准保证的行为? 因为看起来这个Promise对象其实存在引用他的地方 即等待其继续执行的test函数的局部变量中(传入变量) 而test函数任然在"运行"中
这题我会,我来明确告诉你,为什么不会内存泄漏(PS:当然底层实现方法很多,这只是解决永远不会兑现Promise的方法之一)。
直接上源码吧,复杂的东西看源码可能是很简单的实现,C语言写一个Promise底层代码还是比较容易的:

当JavaScript脚本层调用"new Promise"时,虚拟机底层会执行这个promise_proc C语言函数,这个函数很简单,按照注释流程:
1、创建一个pending状态的Promise JSON。
2、创建Promise底层需要的数据结构。这里开辟了新内存。
3、创建resolve和reject参数,通过clang_invoke_tim_function 这个C语言函数,去调用Promise的立即执行函数。
4、这部比较关键:clang_invoke_tim_function 执行Promise的立即执行函数完成后,会去判断执行结果,如果还是pending状态的promise,证明立即执行函数中没有执行resolve。此时还要去判断这个立即执行函数中有没有闭包引用resolve,通过check_child_closure_quote 这个C语言函数去检查resolve闭包引用,这个函数并不复杂,因为编译器在编译阶段,已经确定好了各种闭包的应用关系,只需要循环一次就行了:

5、经过步骤4,如果发现确实没有什么闭包用到resolve,则直接回收内存(这步回收了步骤2所创建的内存),并返回一个特殊的Promise JSON(标记为永不会兑现的Promise JSON)。
6、后面,如果是正常的pending Promise,则加入事件循环,正常执行就好,没什么好说的。
最后再来看,await怎么处理这个”被标记为永不会兑现的Promise“:

说白了就是让async函数直接返回就行了,中间没有任何地方分配内存,自然也不会导致内存泄漏。
实际执行效果:

正常的Promise,没什么好说的
来个大量不正常的:

说实话,async函数连事件循环都进不了,直接返回。
最后再来看一下,Promise本身是否会导致内存泄漏,答案是不会,因为async函数执行完毕后,没什么位置或变量引用他了,自然会被GC清除。
扩展一下,async/await底层实现原理:
JS中的Async/await的底层实现是用Promise吗?