Question presented to candidate: "Explain what problem WeakRef and FinalizationRegistry actually solve, walk through a realistic use case like a client-side cache, and then explain why relying on them for anything time-sensitive or correctness-critical is a real, documented mistake."
What a strong answer should cover:
- A WeakRef holds a reference to an object WITHOUT preventing that object from being garbage collected — .deref() returns the object while it is still reachable elsewhere, and returns undefined once it has actually been collected.
- FinalizationRegistry lets code register a callback that MAY run after a target object has been garbage collected, receiving a "held value" chosen at registration time — verified directly against a real callback firing after real forced garbage collection.
- The core real use case is a cache keyed by object identity where entries should not artificially keep those objects alive — a WeakRef lets the cache check whether a value is still around without itself being the reason it survives.
- Both APIs are explicitly, by specification, NOT guaranteed to run on any particular timeline. The finalization callback might fire much later, might fire in a different order than objects were collected, or in some documented real cases might never fire at all before the process exits — verified directly: identical test code produced different outcomes across separate runs on the same machine.
- register() takes an optional third "unregister token" argument so cleanup can be cancelled early via unregister() — verified directly, and registering a target with itself as the held value is actively rejected with a thrown TypeError, a real spec-enforced guard.
- A widely-cited real-world post from Cloudflare's engineering blog documents production teams being burned by exactly this non-determinism — worth citing as a genuine cautionary case, not just spec text.
Clarifying questions expected:
- "Is this for memory optimization, or for correctness-critical cleanup like closing a file handle or releasing a lock?" — WeakRef/FinalizationRegistry are reasonable for the former and actively dangerous for the latter, since there is no deadline guarantee.
- "Does this code need to behave identically across engines, or is engine-specific timing acceptable?" — GC timing is deeply engine-specific, and even the SAME engine's heuristics differ between a browser tab and a Node.js process.
Code / implementation expected: Yes — real WeakRef/FinalizationRegistry behavior forced and observed via Node's --expose-gc flag, not simulated or assumed timing.