API reference › @evolu/common › Task › Run
Defined in: packages/common/src/Task.ts:578
Runs a Task with structured concurrency semantics.
Each Run forms a Task tree: child Tasks are bound to it, abort propagates
through that tree, and state is observable via snapshots and events.
Run is a callable object — callable because it's convenient to run Tasks as
run(task), and an object because it holds state.
Calling run(task) creates a child Run, passes it to the Task, and returns
a Fiber. The child is tracked in getChildren()/events while running,
then disposed and removed when settled.
Before Task execution, run(task) applies two short-circuit checks:
- If this Run is not
Running, the child is aborted with runStoppedError and the Task is replaced witherr(AbortError). - If this Run's signal is already aborted and the child is abortable
(
abortMask === 0), the child is aborted with the same reason and the Task is replaced witherr(AbortError).
After execution, the child stores both values: outcome (what the Task
returned) and result (what callers observe). If the child signal is aborted
at settlement time, result is forced to err(AbortError) even when
outcome is ok(...).
That's the whole mechanism: Task is a function that takes a Run and
returns an Awaitable Result. run(task) runs the Task via
Promise.try(task, run) with aforementioned logic.
See
Extends
Methods
[asyncDispose]()
asyncDispose: PromiseLike<void>;
Defined in: node_modules/.bun/typescript@5.9.3/node_modules/typescript/lib/lib.esnext.disposable.d.ts:40
Inherited from
AsyncDisposable.[asyncDispose]
Properties
abortMask
readonly abortMask: number & Brand<"Int"> & Brand<"NonNegative"> & Brand<"AbortMask">;
Defined in: packages/common/src/Task.ts:617
The abort mask depth. 0 means abortable, >= 1 means unabortable.
addDeps
readonly addDeps: <E>(extraDeps: E) => Run<D & E>;
Defined in: packages/common/src/Task.ts:801
Adds additional dependencies to this Run and returns it.
Use for runtime-created dependencies — dependencies that cannot be created in the composition root (e.g., app start).
Example
// One-shot
await run.addDeps({ db })(getUser(123));
// Multiple deps at once
await run.addDeps({ db, cache })(task);
// Reusable — config comes from outside (message, file, etc.)
type DbWorkerDeps = DbDep; // or DbDep & CacheDep & ...
const init =
(config: Config): Task<void, InitError, CreateDbDep> =>
async (run) => {
const { createDb } = run.deps;
await using stack = new AsyncDisposableStack();
const db = stack.use(await run.orThrow(startApp()));
if (!db.ok) return db;
const runWithDb = run.addDeps({ db: db.value });
await runWithDb(getUser(123));
await runWithDb(insertUser(user));
return ok();
};
FAQ
How does it work?
This is the whole implementation:
run.addDeps = <E extends NewKeys<E, D>>(newDeps: E): Run<D & E> => {
depsRef.modify((currentDeps) => {
const duplicate = Object.keys(newDeps).find((k) => k in currentDeps);
assert(!duplicate, `Dependency '${duplicate}' already added.`);
return [undefined, { ...currentDeps, ...newDeps }];
});
return self as unknown as Run<D & E>;
};
Dependencies are stored in a shared Ref, so addDeps propagates to
all runs. The runtime assertion ensures dependencies are created once —
automatic deduplication would mask poor design (dependencies should have a
single, clear point of creation).
concurrency
readonly concurrency: Concurrency;
Defined in: packages/common/src/Task.ts:740
See
create
readonly create: () => Run<D>;
Defined in: packages/common/src/Task.ts:731
Creates a Run from this Run.
Like createRun, the returned Run is daemon: it stays running until disposed. Unlike createRun, it shares the same Deps as this Run.
Use this for long-lived disposable resources that need to own async work.
The resource creates one internal Run with run.create() and uses that Run
for all of its work. Disposing the resource then disposes that internal
Run, which aborts in-flight child Tasks, waits for them to settle, and
rejects later calls through it.
Typical examples are database clients, connection pools, workers, or other reusable resources with async methods and an async dispose operation.
To run a single Task as daemon, use Run.daemon.
daemon
readonly daemon: Run<D>;
Defined in: packages/common/src/Task.ts:712
The root Run of this Task tree.
It is called daemon because that is how it should be used: for
long-running work that must not be disposed when the current Task settles.
Normal child Runs are disposed by their parent when they settle. The root
Run has no parent, so work started with run.daemon(task) is attached to
that root Run instead of the current Run and keeps running until the root
Run is disposed manually.
In application code, that usually means disposing the root Run on process shutdown in Node.js or when another platform-specific lifecycle hook is available. Browsers do not provide a fully reliable app termination hook.
Example
const myTask: Task<void, never> = async (run) => {
// Aborted when myTask ends
run(helperTask);
// Outlives myTask, aborted when the root Run is disposed
const backgroundFiber = run.daemon(backgroundSync);
// Can still be aborted manually if needed
backgroundFiber.abort();
return ok();
};
For a long-lived reusable Run, use Run.create.
deps
readonly deps: ConsoleDep & RandomBytesDep & RandomDep & TimeDep & Partial<RunConfigDep> & D;
Defined in: packages/common/src/Task.ts:734
Returns the dependencies passed to createRun.
getChildren
readonly getChildren: () => ReadonlySet<Fiber<any, any, D>>;
Defined in: packages/common/src/Task.ts:645
Returns the current child Fibers.
getState
readonly getState: () => RunState;
Defined in: packages/common/src/Task.ts:642
Returns the current RunState.
id
readonly id: string & Brand<"Id">;
Defined in: packages/common/src/Task.ts:608
Unique Id for this Run.
onAbort
readonly onAbort: (callback: Callback<unknown>) => void;
Defined in: packages/common/src/Task.ts:639
Registers a callback to run when abort is requested.
This is a convenience wrapper around subscribing to this Run's abort signal. The callback receives the abort reason extracted from AbortError.reason rather than the whole AbortError.
If already aborted, the callback is invoked immediately. For unabortable Tasks, the callback is never invoked because their signal never aborts.
Intentionally synchronous and not awaited. The callback runs in the abort
request path, which may already be transitioning this Run to Disposing or
Settled, so it is too late to start normal Tasks from there.
Use for immediate abort-time reactions such as removing listeners, clearing
timers, removing waiters from queues, or resolving pending promises. Do not
use it for awaited cleanup or resource ownership. For that, use standard
JavaScript resource management with AsyncDisposableStack.
onEvent
onEvent:
| ((event: RunEvent) => void)
| undefined;
Defined in: packages/common/src/Task.ts:677
Callback for monitoring Run events.
Called when this Run or any descendant emits a RunEvent. Events
bubble up through parent runs, enabling centralized monitoring. Only
emitted when RunConfig.eventsEnabled is true.
orThrow
readonly orThrow: <T, E>(task: Task<T, E, D>) => Promise<T>;
Defined in: packages/common/src/Task.ts:605
Runs a Task and throws if the returned Result is an error.
Use this where failure should crash the current flow instead of being handled locally.
This is the async equivalent of getOrThrow. It runs the Task, awaits its Result, and returns the value on success.
When to use:
- Application startup or composition-root setup where errors must stop the
program immediately. In Evolu apps, errors are handled by
platform-specific
createRunadapters at the app boundary. - Module-level constants
- Test setup with values that are expected to be valid
Prefer await run(task) with an explicit if (!result.ok) check in
ordinary application logic where the caller can recover, retry, or choose a
different flow.
Throws: Error with the original Task error attached as cause.
parent
readonly parent: Run<D> | null;
Defined in: packages/common/src/Task.ts:611
The parent Run, if this Run was created as a child.
signal
readonly signal: AbortSignal;
Defined in: packages/common/src/Task.ts:614
See
https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal
snapshot
readonly snapshot: () => RunSnapshot;
Defined in: packages/common/src/Task.ts:668
Creates a memoized RunSnapshot of this Run.
Use for monitoring, debugging, or building UI that visualizes Task trees.
Example
// React integration with useSyncExternalStore
const useRunSnapshot = (run: Run) =>
useSyncExternalStore(
(callback) => {
run.onEvent = callback;
return () => {
run.onEvent = undefined;
};
},
() => run.snapshot(),
);