Hit this in production. Comlink.wrap(worker).slow() style calls intermittently hang forever on Chrome with no error and nothing in the console. The trigger is V8 collecting the transient proxy during the suspended await; under memory pressure it fires reliably.
When the proxy is collected, comlink's FinalizationRegistry cleanup runs releaseEndpoint, which sends RELEASE, closes the port, and clears pendingListeners, without checking whether any of those listeners are still mid-flight. The worker's reply lands on a closed port and the Promise never resolves.
MDN is pretty explicit about not relying on FR semantics for correctness:
Developers shouldn't rely on cleanup callbacks for essential program logic.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/FinalizationRegistry
Repro
https://github.com/nyan-left/comlink-repro
Workaround
Pin the proxy on a longer-lived ref so it stays reachable across the await:
// Before, intermittently hangs
const result = await Comlink.wrap(worker).slow();
// After (works):
const proxy = Comlink.wrap(worker);
this._keepAlive = proxy;
try {
return await proxy.slow();
} finally {
this._keepAlive = undefined;
}
Awkward to ask every comlink user to remember though.
Proposed fix
Open PR: #693
Related
Hit this in production.
Comlink.wrap(worker).slow()style calls intermittently hang forever on Chrome with no error and nothing in the console. The trigger is V8 collecting the transient proxy during the suspended await; under memory pressure it fires reliably.When the proxy is collected, comlink's
FinalizationRegistrycleanup runsreleaseEndpoint, which sends RELEASE, closes the port, and clearspendingListeners, without checking whether any of those listeners are still mid-flight. The worker's reply lands on a closed port and the Promise never resolves.MDN is pretty explicit about not relying on FR semantics for correctness:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/FinalizationRegistry
Repro
https://github.com/nyan-left/comlink-repro
Workaround
Pin the proxy on a longer-lived ref so it stays reachable across the await:
Awkward to ask every comlink user to remember though.
Proposed fix
Open PR: #693
Related
releaseProxy(). Related but doesn't cover GC-triggered path.