I asked another question about this, because I didn't see this one. But the discussion here, and on other links that showed up in response to my question, doesn't give a definite answer. (Both yes and no are given as answers, and there's not enough evidence provided to know who's right.)
So I devised this test:
$ node --expose-gc
Welcome to Node.js v17.1.0.
Type ".help" for more information.
> const registry = new FinalizationRegistry(heldValue => { console.log(`finalizing with ${heldValue}`) });
> var stop = false; function loop(res) { if (stop) return res(); setTimeout((() => loop(res)), 3000); }
> var p = new Promise((res,rej) => loop(res)), q = p.then(()=>console.log('done')); registry.register(p, "pho", p); registry.register(q, "qux", q);
> p=q=null;
> gc()
undefined
> gc()
undefined
> gc()
undefined
> gc()
undefined
> gc()
undefined
> stop=true
true
done
> gc()
undefined
finalizing with qux
finalizing with pho
Summary: After p=q=null
I have no references anymore to either of the Promises. So if they were gc'able before resolving, one would expect to see the finalization messages before the loop stopped and the second Promise logged "done". But one doesn't see this.
This isn't conclusive though, since presumably an internal Node registry is holding a reference to the setTimeouts while they're ticking down, and thus also to the res
parameter, and via it to the first Promise. And maybe the first Promise is holding a reference to the second.
I also tried:
> var p = new Promise((res,rej) => { }), q = p.then(()=>console.log('done')); registry.register(p, "pho", p); registry.register(q, "qux", q); q = null;
> gc()
undefined
> gc()
undefined
> gc()
undefined
> gc()
undefined
> p = null
null
> gc()
undefined
finalizing with qux
finalizing with pho
This confirms that as long as you hold a reference to the first Promise, the .then(...)
promise attached to it will be kept alive too, even if you don't have any explicit reference to the latter. But when you drop your reference to the first Promise, then it will become collectable even if not yet resolved, and so too will the second Promise.
If in this last example, I drop the reference to p
first, keeping the reference to q
, then p
becomes collectable. (The reference to q
in that case doesn't keep p
alive.)