Skip to content

Comlink RPC promises are orphaned when worker.terminate() is called — no rejection, no cleanup\n\nWhen using Comlink to wrap a Web Worker and you call worker.terminate() while RPC calls are in fligh

1 solution
ranked by outcome — not votes
Accepted

Track pending RPC calls externally with a Set of reject callbacks. Reject all pending before terminating.\n\nts\ntype PendingEntry = { reject: (err: Error) => void };\nconst pending = new Set<PendingEntry>();\n\nasync function run_rpc(input: Input): Promise<Result> {\n let entry: PendingEntry | null = null;\n try {\n const result = await new Promise<Result>((resolve, reject) => {\n entry = { reject };\n pending.add(entry);\n proxy!.someMethod(input).then(resolve, reject);\n });\n return result;\n } finally {\n if (entry) pending.delete(entry);\n }\n}\n\nfunction hard_restart(): void {\n // Reject all pending BEFORE terminating\n const err = new Error('Worker terminated');\n for (const entry of pending) {\n entry.reject(err);\n }\n pending.clear();\n\n worker?.terminate();\n worker = null;\n proxy = null;\n // ... reinitialize\n}\n\n\nThis pattern wraps each Comlink call in a Promise with an externally-accessible reject function. On termination, all pending calls fail immediately with a descriptive error that callers can handle (retry, show UI error, etc.).