Optionalargs: Argsimport { spawn } from '@datamoc/mwg/threads';
const sum = await spawn((a: number, b: number) => a + b, [40, 2]);
console.log(sum); // 42
//a task that never returns is abandoned rather than hanging the caller
const controller = new AbortController();
const pending = spawn(() => new Promise(() => {}), [], { signal: controller.signal });
controller.abort();
pending.catch((error: Error) => console.log(error.name)); // 'AbortError'
Runs
fnon a worker thread and resolves with its value, keeping the caller's thread free: the one synchronous block a game can never get back is the frame it spends waiting.The task is serialized with
Function.prototype.toStringand rehydrated in the worker, so it must be self-contained - no closures over module scope, no imports, no bound or native functions. Everything it needs arrives asargs, structured-cloned in; buffers listed inoptions.transferare moved instead, detached from the caller's thread. The result comes back structured-cloned, with every ArrayBuffer inside it transferred rather than copied (the worker is finished with them). A task that throws, closes over something the worker cannot see, or exits its worker without answering rejects the promise - it never hangs - andtimeoutorsignalabandon a task that never returns.One worker per task: construction costs a few milliseconds, which is nothing next to the work worth moving off the main thread (T161 measured break-even at roughly 10 ms per task in a batch, and far less against a dropped frame).