workerResource
Run a function off the main thread and read it as a resource. A slow computation makes the UI pending, not frozen.
@mmstack/workernpm
workerResource runs work on a worker and gives you back the same signal surface as a data fetch: value, status, error, isLoading. The first argument reactively derives the input, the run re-fires when that input changes, and the latest run wins.
import { signal } from '@angular/core';
import { workerResource } from '@mmstack/worker';
readonly n = signal(40);
readonly fib = workerResource(() => this.n(), {
worker: this.worker,
task: 'fib',
});
fib.value(); // Signal<number | undefined>, held through re-runs
fib.status(); // 'idle' | 'loading' | 'reloading' | 'resolved' | 'error' | 'local'
fib.isLoading();
fib.error();
fib.reload();
fib.abort(); Because it satisfies the same shape as an @mmstack/resource query, it composes with latest() and use() and registers into transition scopes. Add register: 'indicator' and a boundary above shows a busy state while the run is in flight, holding the previous value.
#The task
The task named in { worker, task } is a function you exposed on the host with createWorkerHost({ tasks }). It runs on the same worker that owns your stores, so a whole subsystem lives on one thread. There is no eval, so nothing changes under a strict CSP.
// runs the 'fib' task exposed by the connected worker host
const fib = workerResource(() => n(), { worker, task: 'fib' });#Latest wins, and holding the value
When the input changes mid-run, the in-flight run is superseded and its result discarded, so the value never regresses. By default the previous value is held through the next run (status reports 'reloading') rather than flashing empty, because a workerResource is a derivation, not a one-shot fetch. Set keepPrevious: false to clear instead.
const users = workerResource(() => query(), {
worker,
task: 'searchUsers',
register: 'indicator', // join a transition scope; boundary shows busy
keepPrevious: true, // default: hold the value while reloading
equal: (a, b) => a.id === b.id, // an equal result emits no notification
});abort() cancels the in-flight run and keeps the current value, moving status to 'local'. reload() re-runs with the current input.
#Pausing
Return ctx.paused from the input function to hold the current value and run nothing. Returning undefined disables the resource (idle). A resume to the same input does not re-run.
const data = workerResource((ctx) => {
if (!ready()) return ctx.paused; // hold the current value, run nothing
if (!id()) return undefined; // idle, disabled
return id();
}, { worker, task: 'load' });#Transferables
For large binary payloads, transfer moves an ArrayBuffer into the worker instead of cloning it, detached at the sender. It applies to a task's input.
import { transfer } from '@mmstack/worker';
const buffer = new Float64Array(1_000_000).buffer;
workerResource(() => transfer({ buffer }, [buffer]), { worker, task: 'process' });