The async utility wraps an async function into a reactive, awaitable controller. You can start, stop, and rerun async operations, and read their state, value, and errors as reactive signals.
that is also callable as a signal: invoking it
(with no args) reads the current result, so it drops into any reactive
context that expects a zero-arg getter.
that is also callable as a signal: invoking it
(with no args) reads the current result, so it drops into any reactive
context that expects a zero-arg getter.
Starts a new reactive async operation, stopping any currently active one.
start(); // begin reactive execution
Control methods
Start for reactive execution, run for one-shot, stop to tear down.
op.start(); // run and track reactive dependencies β reruns when signals change
op.run(); // run once without tracking β does not rerun on signal changes
op.stop(); // stop reactive reruns and run cleanup logic
When to start() vs run()
start() enables fetching automatically. The body is tracked, so the query re-runs whenever a signal it reads before the first await changes. Reach for it when the load is parameterised by reactive state β βfetch user X whenever userId changesβ. The data-fetching recipe uses start() with reactive params.
run() is one-shot and untracked. Reach for it when an external event drives the load β intersection, click, form submit β especially when the body writes to the same signals it reads. The infinite-scroll recipe uses run() for sentinel-driven page loads.
Async implements Symbol.dispose, so using stops it automatically when it goes out of scope:
op.result; // value if fulfilled, reason if rejected, undefined otherwise
op.pending; // true only while a run is in flight
Unlike a bare ReactivePromise (which wraps an already in-flight promise and so
starts pending), an Async is a lazy runner: with no request yet it starts
idle and op.pending is false until you trigger a run. Drive loading UI off
op.pending and it stays quiet until then.
All properties are reactive β reading them inside an effect or computed subscribes to changes.
Callable signal
An Async instance is also callable as a signal. op() returns op.result and tracks it as a reactive dependency:
import { effect } from"elements-kit/signals";
effect(() => {
constresult=op(); // undefined while pending, T when fulfilled, E when rejected
console.log(result);
});
This makes Async composable with computed and templates.
Awaitable
Async implements .then, .catch, and .finally, so you can await it directly:
constop=async(() =>Promise.resolve(123)).start();
constvalue=await op; // 123
Reactive reruns
Read signals inside the async function to make it re-execute when they change. Only signal reads before the first await are tracked.
that is also callable as a signal: invoking it
(with no args) reads the current result, so it drops into any reactive
context that expects a zero-arg getter.
that is also callable as a signal: invoking it
(with no args) reads the current result, so it drops into any reactive
context that expects a zero-arg getter.